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>
53 lines
1.5 KiB
Swift
53 lines
1.5 KiB
Swift
import SwiftUI
|
|
|
|
@main
|
|
struct MarksApp: App {
|
|
@State private var serverConfig: ServerConfig? = ServerConfig.load()
|
|
|
|
var body: some Scene {
|
|
WindowGroup {
|
|
if let config = serverConfig {
|
|
MainContainer(config: config) {
|
|
config.delete()
|
|
serverConfig = nil
|
|
}
|
|
} else {
|
|
OnboardingView { newConfig in
|
|
newConfig.save()
|
|
serverConfig = newConfig
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Holds the API + ViewModel so they survive re-renders
|
|
struct MainContainer: View {
|
|
let config: ServerConfig
|
|
let onDisconnect: () -> Void
|
|
|
|
@State private var viewModel: BookmarksViewModel
|
|
|
|
init(config: ServerConfig, onDisconnect: @escaping () -> Void) {
|
|
self.config = config
|
|
self.onDisconnect = onDisconnect
|
|
let api = LinkdingAPI(config: config)
|
|
let claude = ClaudeService.load()
|
|
_viewModel = State(wrappedValue: BookmarksViewModel(api: api, claude: claude))
|
|
}
|
|
|
|
var body: some View {
|
|
TabView {
|
|
Tab("Bookmarks", systemImage: "bookmark") {
|
|
BookmarksView(viewModel: viewModel, onDisconnect: onDisconnect)
|
|
}
|
|
Tab("Tags", systemImage: "tag") {
|
|
TagsView(viewModel: viewModel)
|
|
}
|
|
Tab(role: .search) {
|
|
SearchView(viewModel: viewModel)
|
|
}
|
|
}
|
|
}
|
|
}
|