mirror of
https://github.com/bretzel-app/crumbs.git
synced 2026-10-13 04:06:57 +02:00
refactor: isolate auth layout, unify tags, add editor animation and pin indicator
- Create (auth) and (app) layout groups to isolate login/setup pages from the main app chrome (header, sidebar, footer) - Use window.location.href instead of goto() after login/setup to ensure clean full-page load across layout groups - Add reusable TagChip component, used in TagFilter and NoteCard - Add fade-in/pop-in entrance animation to note editor overlay - Add persistent clickable pin indicator on pinned cards (top-right bookmark icon, replaces hover-only pin button for pinned notes) - Improve "Add a crumb..." button with + icon and dashed border - Update CLAUDE.md with retro parchment design system guidelines - Add /.claude/ to .gitignore Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
eb18242f5a
commit
f4bd2c12d3
@@ -2,6 +2,7 @@
|
||||
import { NOTE_COLORS } from '$lib/utils/colors.js';
|
||||
import { renderMarkdown } from '$lib/utils/markdown.js';
|
||||
import { togglePin, trashNote, archiveNote, unarchiveNote, restoreNote, deleteNote, updateNote, currentFilter } from '$lib/stores/notes.js';
|
||||
import TagChip from './TagChip.svelte';
|
||||
import type { Note } from '$lib/types/index.js';
|
||||
import Undo2 from 'lucide-svelte/icons/undo-2';
|
||||
import Trash2 from 'lucide-svelte/icons/trash-2';
|
||||
@@ -75,6 +76,17 @@
|
||||
data-testid="note-card"
|
||||
data-note-id={note.id}
|
||||
>
|
||||
{#if note.pinned}
|
||||
<button
|
||||
onclick={stop(() => togglePin(note.id, note.pinned))}
|
||||
class="absolute top-1.5 right-1.5 rounded-sm p-1 text-[var(--primary)] hover:bg-[var(--border)]/10"
|
||||
title="Unpin"
|
||||
data-testid="pin-indicator"
|
||||
>
|
||||
<Bookmark class="h-4 w-4 fill-[var(--primary)]" />
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{#if note.title}
|
||||
<h3 class="mb-2 text-sm font-semibold text-[var(--text)]">{note.title}</h3>
|
||||
{/if}
|
||||
@@ -106,9 +118,7 @@
|
||||
{#if note.tags && note.tags.length > 0}
|
||||
<div class="mt-2 flex flex-wrap gap-1">
|
||||
{#each note.tags as tag}
|
||||
<span class="rounded-sm bg-[var(--border)]/10 px-2 py-0.5 text-xs text-[var(--text-muted)]">
|
||||
#{tag}
|
||||
</span>
|
||||
<TagChip {tag} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -133,14 +143,16 @@
|
||||
<Trash2 class="h-4 w-4 text-[var(--destructive)]" />
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
onclick={stop(() => togglePin(note.id, note.pinned))}
|
||||
class="rounded-sm p-1.5 hover:bg-[var(--border)]/10"
|
||||
title={note.pinned ? 'Unpin' : 'Pin'}
|
||||
data-testid="pin-btn"
|
||||
>
|
||||
<Bookmark class="h-4 w-4 {note.pinned ? 'fill-[var(--primary)] text-[var(--primary)]' : ''}" />
|
||||
</button>
|
||||
{#if !note.pinned}
|
||||
<button
|
||||
onclick={stop(() => togglePin(note.id, note.pinned))}
|
||||
class="rounded-sm p-1.5 hover:bg-[var(--border)]/10"
|
||||
title="Pin"
|
||||
data-testid="pin-btn"
|
||||
>
|
||||
<Bookmark class="h-4 w-4" />
|
||||
</button>
|
||||
{/if}
|
||||
{#if $currentFilter === 'archived'}
|
||||
<button
|
||||
onclick={stop(() => unarchiveNote(note.id))}
|
||||
|
||||
@@ -76,14 +76,14 @@
|
||||
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
class="fixed inset-0 z-40 flex items-start justify-center overflow-y-auto bg-black/50 pt-20 pb-10"
|
||||
class="fixed inset-0 z-40 flex items-start justify-center overflow-y-auto bg-black/50 pt-20 pb-10 animate-[fade-in_150ms_ease-out]"
|
||||
onclick={save}
|
||||
onkeydown={handleKeydown}
|
||||
data-testid="note-editor-overlay"
|
||||
>
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
class="mx-4 flex w-full max-w-xl flex-col rounded-sm border border-[var(--border)]"
|
||||
class="mx-4 flex w-full max-w-xl flex-col rounded-sm border border-[var(--border)] shadow-[var(--card-shadow)] animate-[pop-in_150ms_ease-out]"
|
||||
style={bgStyle}
|
||||
onclick={(e) => e.stopPropagation()}
|
||||
onkeydown={(e) => { e.stopPropagation(); handleKeydown(e); }}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
tag: string;
|
||||
active?: boolean;
|
||||
onclick?: () => void;
|
||||
}
|
||||
|
||||
let { tag, active = false, onclick }: Props = $props();
|
||||
</script>
|
||||
|
||||
{#if onclick}
|
||||
<button
|
||||
{onclick}
|
||||
class="rounded-sm px-2 py-0.5 text-xs font-medium transition-colors {active
|
||||
? 'bg-[var(--primary)] text-white'
|
||||
: 'border border-[var(--border-subtle)] bg-[var(--bg-surface)] text-[var(--text-muted)] hover:border-[var(--primary)]'}"
|
||||
data-testid="tag-chip"
|
||||
>
|
||||
#{tag}
|
||||
</button>
|
||||
{:else}
|
||||
<span
|
||||
class="rounded-sm border border-[var(--border-subtle)] bg-[var(--bg-surface)] px-2 py-0.5 text-xs font-medium text-[var(--text-muted)]"
|
||||
data-testid="tag-chip"
|
||||
>
|
||||
#{tag}
|
||||
</span>
|
||||
{/if}
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { allTags, selectedTag } from '$lib/stores/notes.js';
|
||||
import TagChip from './TagChip.svelte';
|
||||
|
||||
function toggleTag(tag: string) {
|
||||
selectedTag.update((current) => (current === tag ? null : tag));
|
||||
@@ -9,13 +10,7 @@
|
||||
{#if $allTags.length > 0}
|
||||
<div class="flex flex-wrap gap-2" data-testid="tag-filter">
|
||||
{#each $allTags as tag}
|
||||
<button
|
||||
onclick={() => toggleTag(tag)}
|
||||
class="rounded-sm px-3 py-1 text-xs font-medium transition-colors {$selectedTag === tag ? 'bg-[var(--primary)] text-white' : 'border border-[var(--border-subtle)] bg-[var(--bg-surface)] text-[var(--text-muted)] hover:border-[var(--primary)]'}"
|
||||
data-testid="tag-chip"
|
||||
>
|
||||
#{tag}
|
||||
</button>
|
||||
<TagChip {tag} active={$selectedTag === tag} onclick={() => toggleTag(tag)} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user