Every screen now speaks the paper vocabulary rather than only the
bookmarks screen: Search, Sources, Podcasts and the player, Settings,
Add/Edit, Smart Collections, Ask, Onboarding, the browser toolbar, the
Siri snippets, and the share extension's save card.
The change is mostly a design system rather than per-screen tweaks.
LibraryKit gains:
- Semantic colors — secondary/tertiary/faint text, `raised` surfaces,
and accent/alarm/affirm, so screens stop reaching for .secondary,
systemGray, .blue, .red and .green. The three status colors come out
of the palette (the swatch blue, vermilion and forest) rather than
from the system set, so the design keeps spending one set of inks.
- PaperType — the two voices made explicit. Prose (titles, summaries,
anything a person wrote) is serif; anything the machine contributes
(domains, dates, counts, tags, labels, buttons) is monospaced. Keeping
that split strict is what makes the design read as archival rather
than as decoration.
- paperSurface() / paperField() / paperCard() for grounds, inputs and
raised blocks.
- PaperEmptyState, because ContentUnavailableView can't be restyled — it
draws its own bold system type and grey, which was the loudest
remaining system voice once the screens moved onto the sheet. All nine
usages are converted.
The app also sets one accent for the system chrome it doesn't draw —
tab bar, search fields, switches, swipe actions — which otherwise stayed
system blue around paper screens.
BookmarkRow is deleted. Once Search moved to the library row and
TagBookmarksView was gone, nothing passed .classic, so the style fork in
BookmarkListRow went with it. There is one bookmark row now.
The share extension compiles LibraryKit directly, since its save card is
a user-facing surface and should not be the one place still using system
styling.
Verified on device in both color schemes; screens toured with a
temporary UI test harness (removed). Suite passes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JgLHztZGaEHvS3KNeGQmRM
Turns the Podcasts tab into a proper podcast-app experience across three
features that compose on a shared background-generation service.
Background generation (#2)
- New PodcastGenerationManager runs generate→poll→download off the player,
so producing a new episode never stops current playback.
- Headphone taps never interrupt: idle → generate-and-play in the foreground
(unchanged); already playing → generate in the background, episode lands in
the library. A "Generating…" banner surfaces active jobs.
Played/unplayed + queue (#1)
- PodcastEntry gains playedAt (old index.json decodes as unplayed).
- Podcasts tab splits into Up Next / Played with a Play All queue that
auto-advances; finishing marks played; swipe to toggle played state.
Share-extension audio (#3)
- "Create podcast" toggle on the save card, plus configurable Auto-Podcast
Tags in Settings. The extension enqueues a request into the App Group; the
main app drains it on launch/foreground and generates in the background.
Closes#1Closes#2Closes#3
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- bookmarkOnscreen(_:) publishes the viewed bookmark as the on-screen
entity (NSUserActivity.appEntityIdentifier) so Siri can resolve "this"
/ "summarize this one"; applied to the BrowserView sheets in
BookmarksView and SearchView
- Add MarksTests (XCTest) covering entity mapping, EntityIdentifier,
IntentRouter, and the real perform() of SearchMarksIntent and
OpenBookmarkIntent; wired into the Marks scheme test phase
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Expose Marks to Siri, Spotlight, and Shortcuts via App Intents:
- BookmarkEntity (AppEntity + IndexedEntity, keyed on the Linkding
server id) with a server-backed EntityStringQuery for live lookup
- Intents: Add, Open (OpenIntent), Search, ShowUnread, and an AI
Summarize intent backed by ClaudeService; AppShortcutsProvider with
spoken phrases; Siri snippet views and dialog
- SearchMarksIntent conforms to the system.search App Schema
(@AppIntent(schema:), ShowInAppSearchResultsIntent) so it is
executable by conversational Siri / Apple Intelligence
- IntentRouter bridges intents (run in the app process) to the SwiftUI
scene for open/search navigation; widgets refreshed after adds
- Link AppIntents.framework so the metadata processor extracts shortcut
phrases and validates the assistant schema
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Save serverConfig to App Group on every app launch so ShareExtension
can always find credentials without requiring a disconnect/reconnect flow
- Add MarksWidget target with RandomBookmarkWidget (random saved bookmark,
reloads hourly) using file-based App Group storage to avoid CFPreferences issues
- Extract BookmarkListRow as shared component with editorial typography tweaks
(17pt semibold title, 13pt domain, relative date, 32×32 favicon, italic AI summary,
20pt podcast icon, 16pt horizontal insets)
- Add MarksWidget and ShareExtension Xcode schemes for direct run/debug
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Device auto-registers on first launch using hardcoded backend URL
and anon key (MarksAuth.swift). 90-day JWT stored in UserDefaults,
refreshed transparently. No settings fields needed — ClaudeService
is always active and non-optional throughout the app.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Complete ground-up rewrite in SwiftUI targeting iOS 26. Drops the Flutter/Dart
codebase entirely in favour of a lean native app with no third-party dependencies.
Features shipped:
- Bookmark list with pagination, pull-to-refresh, swipe delete/archive
- Add bookmark form + iOS share extension (zero-tap save from any app)
- Tags tab — all tags sorted by count, tap to browse filtered bookmarks
- Native search tab (Tab role: .search) with instant client-side filtering
- AI enrichment via OpenRouter (google/gemini-2.0-flash-lite-001): auto-summary
and tag generation, semantic search, smart collections
- Settings: linkding server config, OpenRouter API key
- App Groups for credential sharing between main app and share extension
- Swift 6 strict concurrency throughout (@Observable, @MainActor)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>