ArrowUp/ArrowDown move focus between active checklist inputs.
At list boundaries, focus stays on the current item (no wrap).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- "Shopping" matched existing "Shopping List" note from another test
- Renamed to "Groceries" to avoid strict mode violation
- Reworded comments: "interleaved" → "checked items before active ones"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The addItem() focus logic used positional indexing (inputs[afterIndex+1])
but afterIndex came from the full items[] array while DOM inputs only
render active (unchecked) items. On reopened notes, checked items are
interleaved in the parsed content, causing the index offset to be wrong.
Fix: use data-item-id attribute for targeted querySelector lookup.
Test: reopened note with interleaved checked/unchecked items.
Introduced in e5688c9 (active/done split).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Wrap readFile in try/catch, return 404 for missing attachment files
- Remove unused CollaboratorPopover import from NoteCard
- Make darkMode a proper $state rune for reactivity (add svelte-runes
polyfill for Vitest so $state works in unit tests)
- Add error toasts for failed share link toggle (non-2xx responses)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The "note cards use dark colors" test was conditional on notes existing,
making the assertion potentially vacuous. Now creates a note first.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use test.describe.serial to prevent parallel preference conflicts
- Set up waitForResponse listener BEFORE click (response may arrive
before listener is registered)
- Add waitForLoadState('networkidle') after navigations
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Switch NoteCard, NoteEditor, and ColorPicker from static NOTE_COLORS
lookups to getNoteColor(color, getIsDarkMode()), so inline background
styles react to the active theme. Also fix the theme preference button
type cast in settings/preferences to resolve the svelte-check error.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds src/lib/utils/theme.svelte.ts with applyTheme() (sets data-theme
attribute and meta theme-color) and getIsDarkMode(). Uses a plain
variable instead of $state since Vitest's node environment does not
compile Svelte 5 runes in .svelte.ts files. Adds jsdom dev dependency
and $app/environment alias in vitest config to support DOM-level unit
tests; fixes preferences.test.ts to include the theme default added
in Task 2.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Address reviewer feedback: null-check meta element, use var(--border-subtle)
for card-shadow, specify .svelte.ts for reactivity, add data-testid attrs,
clarify SSR guards, resolve (auth) layout via root layout, defer landing page.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Design spec for issue #26 — dark parchment theme with system/light/dark
toggle in Settings > Preferences.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
pnpm spawns vite as a grandchild process that SIGTERM doesn't reach,
leaving the CI job hanging after screenshots are captured.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace stdout-based detection with HTTP polling (fetch + retry).
Stdout detection is unreliable in CI where pnpm buffers output.
Increased timeout to 60s.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove title from self-host manifesto note (content-only card)
- Regenerated all 9 screenshots on rebased main
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use @playwright/test instead of playwright (matches devDependencies)
- Use card-specific locators to avoid substring content matches
- Close history panel before editor to avoid overlay blocking
- Add Origin header for multipart upload CSRF compliance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Strip server paths from public attachment responses (new PublicAttachment type)
- Use $page.url.origin instead of window.location.origin (SSR-safe)
- Fix toggle switch left-4.5 → left-[1.125rem] (valid Tailwind)
- Add aria-label to SharingIndicator button for screen readers
- Add opportunistic pruning to IP rate limiter to prevent unbounded growth
- Reduce shared attachment cache to 1 hour (respect share revocation)
- Update OpenAPI schema with PublicAttachment type
- Add unit test asserting server paths are not leaked
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Link "POWERED BY CRUMBS" to crumbs.bretzel.app
- Add dedicated error page for (share) group without "Back to notes"
- Update landing page Note Sharing card to mention public links
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- FEATURES.md: add public link sharing feature details
- ARCHITECTURE.md: add shared_notes table to schema section
- openapi.yaml: add share create/revoke, public note, and public
attachment endpoints with SharedNoteData schema
- Regenerate API.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Unit tests:
- shares-service: create, idempotent create, revoke, get, batch fetch
- ip-rate-limit: under/over limit, IP independence, window reset
E2E tests:
- Create public share link via UI
- Public page renders without auth
- Revoke share returns 404
- Trashed note share returns 404
- Globe icon on shared card
- Copy link to clipboard
Also fix sharing-indicator test-id in existing sharing.spec.ts
and add shared_notes table to test-helpers and db/index.ts init.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Create SharingIndicator component with Globe/Users icon priority
- NoteCard shows Globe icon (gold) for public shares, Users for collabs
- NoteEditor share button reflects public link state
- Share token propagated through store on create/revoke
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add toggle switch, URL display, and copy button for public share
links above the existing collaborators section. Gold border when
active, retro-styled toggle with hard shadows.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- (share) layout group with minimal centered layout
- Server-side load function fetches shared note by token
- Read-only page renders markdown, checklists, and images
- Rate limited via IP-based limiter
- Retro parchment styling with "POWERED BY CRUMBS" footer
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- POST/DELETE /api/notes/[id]/share for creating/revoking public links
- GET /api/shared/[token] for fetching shared note data (no auth)
- GET /api/shared/[token]/attachment/[attachmentId] for public images
- Add /s/ and /api/shared to PUBLIC_PATHS in auth middleware
- Add IP-based rate limiter (60 req/min) for public endpoints
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Create shares-service.ts with fetchSharesForNotes, createShare,
revokeShare, getShareByNoteId, and getSharedNote. Integrate share
tokens into hydrateNotes pipeline alongside tags, attachments,
and collaborators.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add sharedNotes table with noteId primary key, unique token, and
cascade delete. Add NoteShare and SharedNoteData interfaces.
Extend Note type with optional shareToken field.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add max-h-[17rem] overflow-hidden on note cards so checklist notes
don't force text notes in the same grid row to stretch with
truncated content.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>