Skip to content
CAI
Software that uses CAICheck a score

fibelatti/pinboard-kotlin

53.3

Adequate · 22 September 2026

27.7k

lines of production code

Kotlin

primary language

7

measurements over time

CAI band scale
CAI trend line
CAI lens gauges

What this system is

Pinkt is a modern Android application for managing bookmarks and notes across Pinboard and Linkding services. It provides a local-first architecture with offline support, allowing users to save, edit, and search their content with full-text search and saved filters. The app features multi-account management, customizable sync intervals, and the ability to export data or save offline copies of web pages.

How it got here

2019 — Pinkt rebrand and modernization

51 changes.

The project was rebranded to Pinkt and underwent a comprehensive modernization, migrating from Dagger to Hilt, XML to Jetpack Compose, and Gson to Moshi. This period established a multi-service architecture supporting both Pinboard and Linkding, underpinned by a new Ktor-based network layer and a Gradle 9 build system. Significant effort was also dedicated to implementing a unified app state management system and expanding core features like posts, notes, and tag management with robust offline sync capabilities.

2020–2024 — Compose migration and multi-backend support

27 changes.

The project underwent a comprehensive migration to Jetpack Compose and Material 3, introducing a modern UI foundation, reactive state management with StateFlow, and a suite of reusable components. Concurrently, the application expanded its backend support to include Linkding alongside Pinboard, implementing robust offline queuing, customizable sync intervals, and extensive test coverage for the new integrations.

2025–2026 — Compose migration and offline support

21 changes.

The project underwent a comprehensive migration of its UI layer to Jetpack Compose, replacing XML-based implementations for the main activity, navigation, and icons. This architectural shift was accompanied by the introduction of significant new features, including the ability to save and view offline copies of bookmarks and export cached data. The period also saw substantial improvements in build standardization, test coverage, and code quality enforcement.

Features

Add Context, SharedPreferences, and View extension utilities

The core-android extension module now includes new utility functions to simplify common Android development tasks. Context extensions provide a generic \findOwner\ method to traverse the context hierarchy, \getAttributeColor\ to resolve theme attributes to colors, and two \shareText\ overloads to easily trigger system share intents. SharedPreferences handling is streamlined with a generic \put\ function that accepts various types and a typed \get\ function for safer retrieval. Additionally, a \setupLinks\ extension for TextViews automates the process of linkifying URLs and configuring the movement method.

core-android/src/main/java/com/fibelatti/core/android/extension · high confidence

Add Linkding bookmark integration

Users can now sync bookmarks with a Linkding server. This change introduces a new data layer (in the \linkding\ feature module) that uses Ktor for REST API communication and Room for local caching, including full-text search support. It handles bookmark CRUD operations, tag management, and offline persistence via a pending-sync queue, while also supporting Linkding-specific features like archived bookmarks and favicon URLs.

app/src/main/kotlin/com/fibelatti/pinboard/features/linkding · high confidence

Add bookmark export feature

Users can now export their cached bookmarks from Pinboard or Linkding into a local HTML file. The feature includes a ViewModel and UI effects to manage the export flow, such as prompting for a destination file, showing a processing dialog, and displaying success or failure messages. The export logic generates an HTML file with a timestamped name, containing bookmarks from the locally cached data, and supports exporting from both Pinboard and Linkding services if data is available locally.

app/src/main/kotlin/com/fibelatti/pinboard/features/export · high confidence

Add data layer for the Notes feature

Introduced the data-layer components for the new Notes feature, including a Ktor-based API client (NotesApi) and a data source (NotesDataSource) that implements the domain repository interface. The data source handles network calls and maps DTOs to domain models, exposing methods to retrieve all notes or a specific note by ID.

app/src/main/kotlin/com/fibelatti/pinboard/features/notes/data · high confidence

Add offline copy management and viewing

Users can now save web pages for offline access and view them within the app. This change introduces a new Offline Copies section where users can browse saved pages, delete individual copies or perform bulk deletions via a selection mode, and view the content in a dedicated WebView that serves local files securely. The feature includes confirmation dialogs for deletions and handles edge cases like WebView process crashes to ensure a stable offline reading experience.

app/src/main/kotlin/com/fibelatti/pinboard/features/offline/presentation · high confidence

Add offline copy support for posts

Users can now save posts as self-contained offline copies. The new domain layer (OfflineCopyBuilder, OfflineCopyRepository, and SaveOfflineCopy use case) fetches a page, extracts the readable content, and embeds all images as data URIs to ensure the resulting HTML file requires no network access. Copies are stored as single files alongside database records, with automatic cleanup of orphaned files when bookmarks are deleted.

app/src/main/kotlin/com/fibelatti/pinboard/features/offline/domain · high confidence

Add tag renaming capability

Users can now rename existing tags directly within the app. This change introduces a new Compose-based bottom sheet for editing tag names, integrates the rename action into the tag quick-actions menu, and updates the underlying ViewModel to handle the renaming logic and state updates.

app/src/main/kotlin/com/fibelatti/pinboard/features/tags/presentation · high confidence

Added offline copy storage and database support

The app now supports saving offline copies of bookmarks. This change introduces a new file-based storage layer (OfflineCopyStorage) that writes HTML content to local files with atomic updates to prevent corruption, alongside a new Room database table (OfflineCopies) and DAO to track metadata like URLs, titles, and file paths. The implementation includes logic to clean up orphaned files when bookmarks are deleted from the backend, ensuring disk space is managed correctly.

app/src/main/kotlin/com/fibelatti/pinboard/features/offline, app/src/main/kotlin/com/fibelatti/pinboard/features/offline/data · high confidence

Android 17 (API 37) local network access handling and new UI capabilities

This update introduces support for Android 17 (API 37) by implementing a \LocalNetworkAccessProvider\ that detects and requests the new \ACCESS\_LOCAL\_NETWORK\ permission for self-hosted instances on local networks, preventing silent connection timeouts. It also adds a light theme option alongside the existing dark and system default modes, introduces a \yy/MM/dd\ date format preference, improves blockquote rendering with a custom span for older Android versions, and enables Chrome Custom Tabs for link navigation.

app/src/main/kotlin/com/fibelatti/pinboard/core/android · high confidence

App initialization and core infrastructure setup

The application entry point now initializes critical background services, including WorkManager for periodic and pending syncs, and configures the notification system with dedicated channels for share-receiver feedback. It also enforces user preferences for theme/dynamic colors and screenshot protection, while enabling StrictMode and Compose diagnostic tools in debug builds to aid development.

app/src/main/kotlin/com/fibelatti/pinboard · high confidence

Introduce Compose-based core UI components and utilities

This change adds a suite of new Jetpack Compose components and utilities to the app's core UI layer, replacing or supplementing previous implementations. Key additions include a centralized \AppMessageQueue\ and \AppSnackbar\ for displaying transient user notifications, a \CrossfadeLoadingLayout\ for smoother data loading transitions, and an \EmptyListContent\ composable for consistent empty-state displays. The update also introduces \MainTitle\ for the app header, \PullRefreshLayout\ with a scroll-to-top button, \SelectionDialogBottomSheet\ variants for user choices, \SettingToggle\ for preferences, and \TextWithBlockquote\ for rich text rendering. Supporting infrastructure includes \AppTheme\ for dynamic color management, \ErrorHandlerEffect\ for centralized error handling, \JsonSaver\ for state preservation, and \LongClickIconButton\ for enhanced interaction feedback.

app/src/main/kotlin/com/fibelatti/pinboard/core/android/composable · high confidence

Introduce Saved Filters feature

Users can now save and manage custom search filters. This change adds a new Saved Filters screen that displays a list of saved filters, allowing users to view them by tapping and delete them via a long-press action sheet. The feature includes data persistence using Room, a repository layer for managing filter state, and a ViewModel to handle the list of saved filters and deletion actions.

app/src/main/kotlin/com/fibelatti/pinboard/features/filters/presentation · high confidence

Introduce configurable date formatting for bookmarks and notes

The app now uses a new DateFormatter utility to parse and display dates from various sources (such as Pinboard and Linkding) according to the user's preferred format. Users can choose from several display styles (e.g., DD/MM/YY, MM/DD/YY, YY/MM/DD, or YYYY-MM-DD) with or without time, ensuring consistent and personalized date presentation across bookmarks and notes.

app/src/main/kotlin/com/fibelatti/pinboard/core/util · high confidence

Introduce data models and serialization for the Notes feature

Added NoteDto and NoteListDto classes to handle the serialization of note data, replacing previous serialization approaches with kotlinx.serialization. The NoteDto includes fields for id, title, created\_at, updated\_at, and text, while NoteListDto wraps a list of NoteDto objects with a count. A NoteDtoMapper is provided to convert these DTOs into domain Note objects, utilizing a DateFormatter for converting date strings from data format to display format.

app/src/main/kotlin/com/fibelatti/pinboard/features/notes/data/model · high confidence

Introduce dedicated Posts feature screens for managing and viewing bookmarks

This change adds a new presentation layer for the Posts feature, introducing dedicated Compose screens for the bookmark list, bookmark details, and editing bookmarks, alongside supporting components like a description bottom sheet and quick actions. It also includes a new screen for browsing popular bookmarks and the associated ViewModels to handle data flow, enabling users to view, edit, and interact with their saved posts and popular content within a unified, modern UI.

app/src/main/kotlin/com/fibelatti/pinboard/features/posts/presentation · high confidence

Introduce domain model and repository for the Notes feature

This change adds the foundational domain layer for the new Notes feature. It defines the \Note\ data class containing fields for identifiers, text content, and creation/update timestamps. A \NotesRepository\ interface is introduced to handle fetching notes, utilizing Kotlin's standard \Result\ type for error handling. Additionally, a \NoteSorting\ sealed class is added to support sorting notes by update date (ascending/descending) or alphabetically.

app/src/main/kotlin/com/fibelatti/pinboard/features/notes/domain · high confidence

Introduce domain repositories for tag management

New domain-layer interfaces, TagManagerRepository and TagsRepository, have been added to the tags feature. TagManagerRepository exposes a state flow for managing tag collections (including adding, removing, and searching) and defines the TagManagerState data class, while TagsRepository provides methods to retrieve all tags and rename them. These interfaces establish the contract for the tag management domain logic.

app/src/main/kotlin/com/fibelatti/pinboard/features/tags/domain · high confidence

Introduction of Tag domain model and sorting options

The tags feature now includes a dedicated Tag domain model class, serializable via kotlinx-serialization, which stores a tag's name and associated post count. Additionally, a new TagSorting sealed class has been introduced to support user-defined sorting preferences, specifically allowing tags to be ordered alphabetically (A-Z), by post count descending (More First), or by post count ascending (Less First).

app/src/main/kotlin/com/fibelatti/pinboard/features/tags/domain/model · high confidence

Introduction of saved filters domain model and repository

The application now supports saving user-defined filters. This change introduces the SavedFilter domain model, which stores a search term, a list of associated tags, and options for matching all tags or using exact matching. A corresponding SavedFiltersRepository interface is added to manage the persistence of these filters via Kotlin coroutines flows.

app/src/main/kotlin/com/fibelatti/pinboard/features/filters/domain · high confidence

New Compose UI components and bottom sheet management

The UI component library has been expanded with several new Compose-based elements: a custom AppBottomSheet that manages visibility and state across configuration changes, a ChipGroup with both single-line (LazyRow) and multiline (FlowRow) layouts, a RadioGroup for selectable lists, a ListItem with configurable height and shape, a ConnectedButtonRowItem for grouped toggle buttons, a ButtonToggleGroup for segmented controls, an AutoSizeText component for dynamic font scaling, and a TextWithLinks component that detects and handles URL taps. These additions provide a richer set of reusable UI primitives for building the application's interface.

ui/src/main/java/com/fibelatti/ui/components · high confidence

New ResultFrom utility for safe coroutine execution

A new \resultFrom\ extension function has been added to the core functional module, providing a standardized way to execute suspend blocks and capture their outcomes as Kotlin \Result\ objects. This utility internally leverages \coRunCatching\ for exception handling and integrates the updated \retry\ logic, ensuring that transient failures are handled gracefully before returning the final result to the caller.

app/src/main/kotlin/com/fibelatti/pinboard/core/functional · high confidence

New background and foreground share receiver flows

The app now supports saving shared URLs in the background via a WorkManager worker, which posts notifications for success, failure, or when the URL already exists. If background saving is not eligible (e.g., multiple accounts connected or permission missing), the app falls back to a foreground Compose-based activity that lets the user choose between Pinboard and Linkding, or edit the bookmark before saving.

app/src/main/kotlin/com/fibelatti/pinboard/features/share · high confidence

New dependency injection modules for multi-service networking and app state

The app introduces a comprehensive set of Hilt modules in the core DI layer to support a multi-service architecture and a unidirectional data flow. NetworkModule and service-specific modules (PinboardModule, LinkdingModule) configure Ktor HTTP clients with distinct behaviors: the Pinboard client includes a response validator to handle 500 errors gracefully, while the Linkding client allows cleartext and custom SSL for self-hosted instances. The Website client is configured to allow HTTPS downgrades for fetching arbitrary URLs. AppStateModule and MainModule wire up a map of action handlers and reducers for various screens (posts, tags, notes, offline copies, filters), binding them to the app state flow. DatabaseModule initializes Room with destructive migration support and provides DAOs for the new features.

app/src/main/kotlin/com/fibelatti/pinboard/core/di/modules · high confidence

New domain models and repository interface for post management

The posts domain layer now includes new domain models and a repository interface to support enhanced post management capabilities. \EditAfterSharing\ defines when editing occurs relative to saving, \PostVisibility\ manages public/private/none states, and \PreferredDetailsView\ configures whether posts open in an in-app browser, external browser, or editor, with options to mark as read on open. The \PostsRepository\ interface exposes methods for adding, deleting, archiving, and retrieving posts with extensive filtering (tags, search, visibility, read status, archive status) and pagination, along with support for pending sync operations and cache clearing.

app/src/main/kotlin/com/fibelatti/pinboard/features/posts/domain · high confidence

New functional core abstractions and utilities

The core module now includes a suite of new functional programming utilities and architectural interfaces. This introduces coroutine-safe error handling via \coRunCatching\, \coMapCatching\, and \coRecoverCatching\ which preserve \CancellationException\ semantics, and a configurable \retry\ function with exponential backoff. A new \ScreenState\ sealed interface and associated Flow extensions provide a standardized pattern for managing UI loading, loaded, and error states. Additionally, the module adds \UseCase\ functional interfaces (\UseCase\, \ResultUseCase\, etc.) to enforce single-responsibility use case definitions, a \SingleRunner\ for sequential coroutine execution, a custom \Either\ type for functional error handling, and various extension functions like \ifNullOrBlank\ and \safeLet\.

core/src/commonMain/kotlin/com/fibelatti/core/functional · high confidence

New manifest permission validation and standardized build convention plugins

The build system now includes a new \ManifestPermissionValidationPlugin\ that automatically checks Android manifest permissions against a generated baseline during the \check\ task, failing the build if unexpected permissions are added, removed, or changed; developers can regenerate the baseline using the new \generate{Variant}ManifestBaseline\ tasks. Additionally, the \build-logic/convention\ module introduces a suite of new convention plugins (\AndroidApplicationConventionPlugin\, \AndroidBaseConventionPlugin\, \AndroidComposeConventionPlugin\, \AndroidLibraryConventionPlugin\, \KotlinLibraryConventionPlugin\, and \SpotlessPlugin\) that standardize Android and Kotlin project configurations, including SDK compatibility, core library desugaring, Compose setup, explicit API modes, JUnit Platform test execution, and Spotless code formatting with ktlint compose rules.

build-logic/convention · high confidence

New platform abstraction layer for Android resources and intent handling

This change introduces a new \core-android\ platform module containing foundational utilities for Android development. It adds a \ResourceProvider\ interface and its \AppResourceProvider\ implementation, which abstracts string and plural resource retrieval (including locale-aware context updates) and asset reading to improve testability. Additionally, it provides Kotlin property delegates (\bundleDelegate\, \intentExtras\, \fragmentArgs\) for concise access to Bundle, Intent, and Fragment arguments, as well as a \BaseIntentBuilder\ for constructing Intents with standard flags and a \SimpleActivityLifecycleCallbacks\ stub for monitoring activity states.

core-android/src/main/java/com/fibelatti/core/android/platform · high confidence

New preview annotations for accessibility, devices, locales, and themes

The UI module now includes a set of new Compose preview annotations to simplify rendering components in various contexts. Developers can use \PreviewAccessibility\ to check font scaling, \PreviewDevices\ to view layouts on specific devices like Pixel 9 or tablets, \PreviewLocales\ to test German, French, and Spanish translations, and \PreviewThemesAndColors\ to verify light/dark modes and dynamic color wallpapers. These can be combined using the \PreviewAll\ annotation for comprehensive preview coverage.

ui/src/main/java/com/fibelatti/ui/preview · high confidence

New user preferences for appearance, sync, and bookmarking behavior

The app now supports a wide range of new user preferences stored in SharedPreferences, allowing users to customize their experience. Key additions include controls for periodic sync intervals, dynamic color application, and screenshot disabling. Users can also configure how dates are displayed (including time inclusion), manage default flags for new bookmarks (private, read-later, tags), and adjust UI behaviors such as split navigation, mark-as-read-on-open, and follow-redirects settings. Additional options allow for customizing post quick options, blockquote usage, and URL parameter handling.

app/src/main/kotlin/com/fibelatti/pinboard/core/persistence · high confidence

Support for Linkding bookmark service alongside Pinboard

The app now supports connecting to Linkding in addition to Pinboard. A new AppMode enum defines the available services (Pinboard, Linkding, No API, Unset), and the AppModeProvider logic has been updated to detect and switch between these modes based on the user's stored authentication tokens. This allows users to manage bookmarks from a Linkding instance using the same local-first architecture previously used for Pinboard.

app/src/main/kotlin/com/fibelatti/pinboard/core · high confidence

Support for Linkding integration and expanded user preferences

The application now supports connecting to Linkding instances alongside Pinboard, introducing multi-account capabilities and specific settings for Linkding such as instance URL and client certificate alias (mTLS). This change expands the user preferences model to include new controls for dynamic colors, screenshot prevention, date formatting, sort types, and background sharing, while updating the authentication flow to handle both service types and persist their respective credentials.

app/src/main/kotlin/com/fibelatti/pinboard/features/user/domain · high confidence

Support for multi-account management and Linkding integration

The app now supports managing multiple accounts across Pinboard and Linkding services. Users can add, switch between, and log out of accounts via the new Account Switcher screen. The authentication flow has been updated to support Linkding, including fields for instance URLs and client certificate (mTLS) selection. Additionally, the User Preferences screen now includes settings to control periodic sync, appearance, and other user-specific configurations, with the ability to clear offline copies.

app/src/main/kotlin/com/fibelatti/pinboard/features/user/presentation · high confidence

Removals

Removal of legacy build configuration files

The \AppInfo\ and \Dependencies\ Kotlin objects in \buildSrc\ have been removed. This eliminates the centralized definitions for the app name, application ID, versioning logic, SDK targets, and all third-party library versions (including Dagger, Moshi, Coroutines, and testing libraries) that were previously managed in this module.

buildSrc · high confidence

Architecture

Introduces a unified app state management system with typed actions and handlers

The app now uses a centralized state model (AppState) driven by a unidirectional data flow. All user interactions and system events are expressed as typed Actions (e.g., NavigationAction, PostAction, SearchAction), which are processed by specific ActionHandlers to produce immutable Content updates. This replaces the previous state management approach, providing a more predictable and testable way to handle app state transitions, navigation, and data loading across the application.

app/src/main/kotlin/com/fibelatti/pinboard/features/appstate · high confidence

Behavioural changes

4 commits (0 fixes) modifying app/libs

A change to existing behaviour in app/libs — 4 commits, 2 files.

app/libs · medium confidence · unverified

Added pre-push hook to run Spotless checks

A new pre-push git hook has been added to the repository. When a user attempts to push code, the system will automatically run the Spotless code formatter check via Gradle before allowing the push to proceed, helping to enforce code style consistency.

.githooks · high confidence

App icons migrated to Compose ImageVector

The app's icon resources have been replaced with Compose-native ImageVector definitions. This change updates the icon system to use vector graphics defined in Kotlin, which improves rendering performance and allows for easier theme integration compared to the previous XML-based resources.

app/src/main/kotlin/com/fibelatti/pinboard/core/android/icons · high confidence

Customizable sync intervals and offline change queuing

Users can now customize how often the app syncs with the server (off, every 6, 12, or 24 hours) via the new PeriodicSync settings, and changes made while offline are automatically queued and applied once connectivity is restored through the new PendingSyncManager and PendingSyncWorker.

app/src/main/kotlin/com/fibelatti/pinboard/features/sync · high confidence

Database schema upgrade to version 10 with offline copy support

The local Room database has been upgraded from version 9 to version 10 to support offline copies. This change introduces the \OfflineCopyDto\ entity and the corresponding \OfflineCopiesDao\ to the database definition, enabling the app to store and manage offline data. The migration from the previous version is handled automatically via an \AutoMigration\ configuration. Additionally, the database now includes entities for saved filters and FTS tables for posts and bookmarks, supporting advanced search and filtering capabilities.

app/src/main/kotlin/com/fibelatti/pinboard/core/persistence/database · high confidence

Introduce BaseViewModel with StateFlow and coroutine scope management

The app introduces a new BaseViewModel class in the core android base package that serves as a foundation for view models. This class manages application state and error handling using Kotlin's StateFlow instead of LiveData, providing a more modern reactive approach. It also standardizes coroutine scope usage by deriving the scope from the ViewModel's built-in viewModelScope, ensuring proper lifecycle management. The base class provides utilities for filtering content, running actions with immediate or delayed dispatch, and handling errors in a way that integrates better with Jetpack Compose.

app/src/main/kotlin/com/fibelatti/pinboard/core/android/base · high confidence

Introduces new data models and mappers for posts and API responses

This change introduces a new set of data transfer objects (DTOs) and mappers within the posts feature's data layer to handle API communication and local storage. Specifically, it adds \GenericResponseDto\ and \UpdateDto\ for parsing API result codes and update timestamps, \GetPostDto\ for retrieving post lists, and \PendingSyncDto\ to track offline actions (add, update, delete, archive). The core \PostDto\ is defined as a Room entity with indices on 'shared' and 'toread' fields, mapped to the domain \Post\ model via \PostDtoMapper\, which handles field translation (e.g., \hash\ to \id\, boolean flags for private/read-later) and HTML character replacement. A separate \PostRemoteDto\ and its mapper handle raw API data, providing defaults for nullable fields. Additionally, a \PostDtoFts\ entity is added to support Full-Text Search (FTS4) with a Unicode61 tokenizer, enabling faster text-based queries on post content and tags.

app/src/main/kotlin/com/fibelatti/pinboard/features/posts/data/model · high confidence

Main feature area migrated to a new Compose-based architecture

The main feature area has been rebuilt using Jetpack Compose, replacing the previous implementation. This change introduces a new \MainComposeActivity\ to handle the application entry point, deep links, and local network permissions, alongside a new \MainScreen\ composable that manages the UI layout, navigation, and state. The \MainViewModel\ and \MainState\ have been updated to support this new structure, including specific handling for multi-panel availability and scroll direction, ensuring the core browsing and bookmarking experience functions within the new Compose framework.

app/src/main/kotlin/com/fibelatti/pinboard/features/main · high confidence

Migrate icon definitions to Compose ImageVector

The icon resources in the UI module have been converted from XML-based assets to Jetpack Compose ImageVector definitions. This change replaces the previous XML resource approach with programmatic vector graphics, allowing icons like 'Close' to be rendered directly within Compose UI without external resource loading.

ui/src/main/java/com/fibelatti/ui/icons · high confidence

Migrate licenses screen to Compose and aboutlibraries-compose-m3

The licenses screen has been rewritten in Jetpack Compose and now uses the \com.mikepenz:aboutlibraries-compose-m3\ library (upgraded from version 14.2.1 to 15.0.2). This change replaces the previous XML-based implementation with a modern UI that integrates with the app's Material 3 theme, supports edge-to-edge display, and loads library data from a manually generated JSON resource (\R.raw.aboutlibraries\).

app/src/main/kotlin/com/fibelatti/pinboard/features/licenses · high confidence

Migrate to Material 3 Expressive Theme with custom color scheme

The app's UI theme has been updated to use Material 3's Expressive theme, introducing a new blue-dominant color palette for both light and dark modes and enabling dynamic colors on Android 12+. A new ExtendedTheme composable now wraps the application, providing access to extended colors (such as backgroundNoOverlay) and custom button shapes, replacing the previous theme implementation.

ui/src/main/java/com/fibelatti/ui/theme · high confidence

The navigation menu has been rewritten using Jetpack Compose, replacing the previous XML-based implementation. This update introduces support for archived bookmarks and offline copies, adds an option to export local bookmark caches, and refreshes the visual appearance by using ImageVector icons and standardized spacing. The menu now includes dedicated actions for sending feedback, writing reviews, sharing the app, and viewing privacy policies and open-source licenses.

app/src/main/kotlin/com/fibelatti/pinboard/features/navigation · high confidence

Network layer refactored to Ktor with mTLS and resilience improvements

The network module has been migrated to Ktor, introducing support for mutual TLS (mTLS) client certificates for Linkding via new key management and socket factory components. To improve reliability, the layer now implements exponential backoff for 429 (Too Many Requests) errors and includes a plugin to sanitize malformed Pinboard API responses. Additionally, custom exception types and result-handling utilities have been added to standardize error reporting and network retry logic.

app/src/main/kotlin/com/fibelatti/pinboard/core/network · high confidence

New DI map keys for action handlers and main reducer

The application now uses specific Dagger MapKey annotations to support multibinding for action handlers and the main reducer. A new \ActionHandlerKey\ allows binding action handlers by their \Action\ type, while \MainReducerKey\ enables binding the main reducer by \ContentType\. These changes facilitate more granular dependency injection for state management components.

app/src/main/kotlin/com/fibelatti/pinboard/core/di/mapkeys · high confidence

New UI foundation utilities and consistent corner radii

The UI foundation layer now includes helper functions for density conversion (Dp to Px and vice versa) and a new \PaddingValues.copy\ utility to simplify padding manipulation. A \rememberKeyboardState\ composable has been added to track keyboard visibility and animation states (closed, opening, open, closing). Additionally, a \Shapes\ object defines consistent rounded corner radii (16dp for medium, 4dp for small) for various UI elements like top, bottom, start, end, middle, and standalone shapes, ensuring a unified visual style across the app.

ui/src/main/java/com/fibelatti/ui/foundation · high confidence

New core extension utilities for UI, networking, and security

This update introduces a suite of new Kotlin extension functions in the core module to support the app's migration to Jetpack Compose and Ktor. Users benefit from improved scroll detection in lists via \rememberScrollDirection\, which now uses \produceState\ to avoid memory leaks. The app now handles unescaped HTML characters from the API via new string utilities. Security is enhanced with a new setting to prevent screenshots in dialogs and automatic redaction of auth tokens in error reports. Additionally, error handling for server connectivity issues has been expanded to cover more Ktor-specific exceptions and certificate validation failures.

app/src/main/kotlin/com/fibelatti/pinboard/core/extension · high confidence

New domain models for posts and synchronization state

The application introduces new domain models to represent posts and their synchronization status. The \Post\ model now includes fields for added and modified dates, optional private and read-later flags, and website metadata, with logic to resolve favicons using a Gstatic fallback when not provided. A new \PendingSync\ enum defines local actions (add, update, delete, archive, unarchive) to support offline operations, and \PostListResult\ wraps bookmark lists with pagination and sync status information.

app/src/main/kotlin/com/fibelatti/pinboard/features/posts/domain/model · high confidence

Notes screens migrated to Jetpack Compose

The Note List and Note Details screens have been rewritten using Jetpack Compose, replacing the previous XML-based implementations. This change introduces a modern UI layer with Material 3 theming, including a new sorting interface in the list view and a scrollable detail view. The underlying ViewModels have also been updated to use Hilt for dependency injection and StateFlow for state management, ensuring consistent behavior across the notes feature.

app/src/main/kotlin/com/fibelatti/pinboard/features/notes/presentation · high confidence

Platform abstraction interfaces moved to core module

The ConnectivityInfoProvider and UserAgentProvider interfaces have been relocated to the core module's platform package. ConnectivityInfoProvider is now a functional interface exposing isConnected(), while UserAgentProvider remains an interface exposing the userAgent property, making these platform-specific abstractions available for shared code consumption.

core/src/commonMain/kotlin/com/fibelatti/core/platform · high confidence

Posts data layer refactored to support multiple backends and offline sync

The posts data layer has been restructured to support Pinboard, Linkding, and a local-only mode via a new \PostsDataSourceProxy\ that routes requests to the appropriate implementation. The local data source now supports offline add, edit, and delete operations with pending sync tracking, while the Pinboard API source handles remote synchronization and fallback to local storage on network failure. Search performance is improved through Full-Text Search (FTS) integration in the Room database, allowing for faster multi-term and tag-based queries. The API client has been migrated to Ktor, and pagination logic has been updated to support offset-based fetching.

app/src/main/kotlin/com/fibelatti/pinboard/features/posts/data · high confidence

Project rebrand to Pinkt and migration to modern Android tooling

The application has been rebranded from Pinboard Kotlin to Pinkt and now supports both Pinboard and Linkding services. The codebase has been modernized by migrating from Dagger to Hilt for dependency injection, adopting Jetpack Compose with Material 3 for the UI, and switching from Gson to Moshi for JSON serialization. Build tooling has been upgraded to Gradle 9, and CI infrastructure has moved from CircleCI to GitHub Actions. Additionally, the project now includes an in-app review feature, an IzzyOnDroid flag, and automated signed releases.

(repo-wide) · high confidence

Refactor main UI state management into dedicated reducers

The main feature's UI state logic has been restructured from a monolithic approach into a set of specialized reducers (e.g., BookmarkListReducer, SearchReducer, NoteListReducer) that implement the new MainStateReducer interface. This change ensures that the app's navigation, titles, action buttons, and bottom app bars are now derived strictly from the current AppState content, providing a more consistent and predictable user interface across different views such as bookmark lists, search results, and offline copies.

app/src/main/kotlin/com/fibelatti/pinboard/features/main/reducer · high confidence

Refactored post management and URL handling into new domain use cases

The post domain layer has been restructured into a set of dedicated use cases to improve code organization and behavior. CRUD operations (Add, Archive, Delete, Unarchive) now explicitly validate URLs before persisting changes to the repository. URL processing is handled by ExtractUrl, which supports extracting highlighted text from shared URLs and offers a new setting to automatically strip UTM and other query parameters. Fetching logic is centralized in GetAllPosts and GetRecentPosts, which now support advanced filtering (tags, search, visibility, archived status) and pagination (offset/limit). Additionally, GetUrlPreview has been rewritten to use the Ktor HTTP client and Jsoup for robust HTML parsing, introducing configurable behavior for following redirects and auto-filling descriptions, while GetPopularPosts now fetches trending bookmarks directly from Pinboard.in.

app/src/main/kotlin/com/fibelatti/pinboard/features/posts/domain/usecase · high confidence

Simplified ProGuard configuration for Ktor and Compose

The app's ProGuard rules have been significantly streamlined to reflect the migration to Ktor and the use of the new Compose Slot Table. Moshi-specific keep rules have been removed, while new directives have been added to suppress warnings for Ktor, OkHttp, and jsoup dependencies. Additionally, the configuration now explicitly enables the Compose runtime link buffer composer flag in release builds and updates Kotlin coroutine handling rules.

app · high confidence

Tag management now supports renaming and multi-backend synchronization

The tag data layer has been refactored to support renaming tags across Pinboard and Linkding backends, in addition to local-only mode. A new proxy data source routes requests to the appropriate implementation based on the current app mode, while the Pinboard-specific data source now calls the API to rename tags and updates local state accordingly. The tag manager data source has also been updated to streamline state flow collection, fixing a previous flow collection leak and ensuring tag suggestions are updated correctly based on the current post context.

app/src/main/kotlin/com/fibelatti/pinboard/features/tags/data · high confidence

Updated app icons and dependency license metadata

The app's launcher icon has been updated with a new foreground vector graphic, and a dedicated pin icon resource has been added for UI use. Additionally, the \aboutlibraries.json\ file has been refreshed to reflect the current versions of third-party dependencies, including AndroidX Compose components and Material Components, ensuring the in-app open-source license viewer displays accurate attribution data.

app/src/main/res · high confidence

User preferences and authentication settings are now persisted locally

The app now stores user-specific settings—such as appearance mode, sort order, date formats, and mTLS client certificate aliases—directly in local shared preferences via the new UserDataSource. This change ensures that user customizations and authentication details are retained across sessions and are no longer dependent solely on remote state or transient memory.

app/src/main/kotlin/com/fibelatti/pinboard/features/user/data · high confidence

Test coverage

Added end-to-end instrumented tests for Pinboard and Linkding integrations; Added instrumented tests for Linkding BookmarksDao; Added instrumented tests for the Posts DAO; Added test infrastructure and utilities for the Pinboard app; Added tests for FTS language compatibility check; Added tests for GetExportableServicesUseCase; Added tests for Saved Filters DAO; Added tests for core extension and functional utilities; Added tests for network error retry logic; Added tests for offline copy data access; Added tests for string HTML handling and exception classification extensions; Added tests for the offline copy feature; Added unit tests for DateFormatter; Added unit tests for Linkding bookmark data mapping and archive operations; Added unit tests for MainViewModel and UserPreferencesReducer; Added unit tests for NoteDetails and NoteList ViewModels; Added unit tests for NoteDtoMapper; Added unit tests for NotesDataSource; Added unit tests for PostDtoMapper; Added unit tests for ShareReceiverViewModel; Added unit tests for TagsViewModel; Added unit tests for UserDataSource; Added unit tests for UserSharedPreferences; Added unit tests for app state management and action handlers; Added unit tests for core Android components; Added unit tests for core configuration and app mode logic; Added unit tests for posts data sources; Added unit tests for posts domain use cases; Added unit tests for posts presentation ViewModels; Added unit tests for sync workers; Added unit tests for tag data sources; Added unit tests for the Saved Filters feature; Added unit tests for user domain logic; Instrumented test infrastructure migrated to Ktor with mock server support; New test fixtures for mock data and random generation; Removed Mockito inline mock maker configuration.

Dependencies

Migrate build system to Gradle version catalog and convention plugins

The project has replaced the legacy Groovy build scripts and \buildSrc\ with a modern Kotlin DSL setup using a central \libs.versions.toml\ for dependency management and a \build-logic\ convention plugin module. This migration introduces the Android Gradle Plugin 9.4.1, Kotlin 2.4.20, and KSP 2.3.12, while removing deprecated tools like Jacoco, Detekt, and Ktlint in favor of Spotless and Licensee. The app module now uses namespaced builds, explicit dependency declarations via the catalog, and supports multiplatform core modules, resulting in a more maintainable and type-safe build configuration.

(dependencies) · high confidence

Upgrade Gradle wrapper to version 9.7.1

The project's Gradle wrapper has been upgraded from version 4.10.2 to 9.7.1. This significant version jump introduces enhanced build reliability through new configuration properties: SHA-256 distribution validation is now enforced, network timeouts are explicitly set to 10 seconds, and retry logic with a 500ms back-off is configured to handle transient distribution download failures.

gradle · high confidence

Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.

How this codebase got here

Score

  • CAI 55 → 53 (-1.2)
  • Rubric changed (rubric-2026.08.19 → rubric-2026.09.15) — scores are not directly comparable.

Lenses

  • Code Health 94 → 95 (+1.5)
  • Architecture 95 → 92 (-3.1)
  • Maturity 58 → 58 (-0.6)
  • Readiness 61 → 49 (-12.1)
  • Security 43 → 48 (+5.3)
  • Domain Modelling 70 → 60 (-9.9)

Resolved (48)

  • Change coupling: BookmarksDao.kt ↔ PostsDao.kt (app/src/main/kotlin/com/fibelatti/pinboard/features/linkding/data/BookmarksDao.kt)
  • Change coupling: EmptyListContent.kt ↔ TagList.kt (app/src/main/kotlin/com/fibelatti/pinboard/core/android/composable/EmptyListContent.kt)
  • Change coupling: PostsDataSourcePinboardApi.kt ↔ GetAllPosts.kt (app/src/main/kotlin/com/fibelatti/pinboard/features/posts/data/PostsDataSourcePinboardApi.kt)
  • Change coupling: PostsDataSourcePinboardApi.kt ↔ GetRecentPosts.kt (app/src/main/kotlin/com/fibelatti/pinboard/features/posts/data/PostsDataSourcePinboardApi.kt)
  • Change coupling: UserDataSource.kt ↔ UserPreferencesProvider.kt (app/src/main/kotlin/com/fibelatti/pinboard/features/user/data/UserDataSource.kt)
  • Change coupling: UserDataSource.kt ↔ UserPreferencesViewModel.kt (app/src/main/kotlin/com/fibelatti/pinboard/features/user/data/UserDataSource.kt)
  • Change coupling: UserSharedPreferences.kt ↔ UserPreferences.kt (app/src/main/kotlin/com/fibelatti/pinboard/core/persistence/UserSharedPreferences.kt)
  • Change coupling: UserSharedPreferences.kt ↔ UserPreferencesProvider.kt (app/src/main/kotlin/com/fibelatti/pinboard/core/persistence/UserSharedPreferences.kt)
  • Coverage not included — suite not readable by the collector
  • Dependency hygiene not measured — dependency manifest found but not parsed for hygiene
  • Duplicated block (10 lines × 2) (app/src/main/kotlin/com/fibelatti/pinboard/features/linkding/data/PostsDataSourceLinkdingApi.kt)
  • Duplicated block (10 lines × 2) (app/src/main/kotlin/com/fibelatti/pinboard/features/posts/data/PostsDataSourceNoApi.kt)
  • Duplicated block (10 lines × 2) (app/src/main/kotlin/com/fibelatti/pinboard/features/posts/presentation/SearchBookmarksScreen.kt)
  • Duplicated block (12 lines × 2) (app/src/main/kotlin/com/fibelatti/pinboard/features/posts/presentation/BookmarkDetailsScreen.kt)
  • Duplicated block (13 lines × 2) (app/src/main/kotlin/com/fibelatti/pinboard/features/notes/presentation/NoteListScreen.kt)
  • Duplicated block (13 lines × 2) (app/src/main/kotlin/com/fibelatti/pinboard/features/posts/data/PostsDataSourceNoApi.kt)
  • Duplicated block (13 lines × 5) (app/src/main/kotlin/com/fibelatti/pinboard/features/user/presentation/AccountSwitcherScreen.kt)
  • Duplicated block (14 lines × 2) (app/src/main/kotlin/com/fibelatti/pinboard/features/posts/data/PostsDataSourceNoApi.kt)
  • Duplicated block (16 lines × 2) (app/src/main/kotlin/com/fibelatti/pinboard/features/linkding/data/BookmarksDao.kt)
  • Duplicated block (19 lines × 4) (app/src/main/kotlin/com/fibelatti/pinboard/features/linkding/data/BookmarksDao.kt)
  • …and 28 more

New (91)

  • Change-coupling hub: BookmarksDao.kt → PostsDao.kt, PostsDataSourceNoApi.kt, PostsDataSourcePinboardApi.kt (app/src/main/kotlin/com/fibelatti/pinboard/features/linkding/data/BookmarksDao.kt)
  • Change-coupling hub: GetAllPosts.kt → Action.kt, PostsDataSourceNoApi.kt, PostsDataSourcePinboardApi.kt (app/src/main/kotlin/com/fibelatti/pinboard/features/posts/domain/usecase/GetAllPosts.kt)
  • Change-coupling hub: GetRecentPosts.kt → Action.kt, PostsDataSourceNoApi.kt, PostsDataSourcePinboardApi.kt (app/src/main/kotlin/com/fibelatti/pinboard/features/posts/domain/usecase/GetRecentPosts.kt)
  • Change-coupling hub: UserDataSource.kt → Login.kt, UserPreferencesProvider.kt, UserPreferencesViewModel.kt (app/src/main/kotlin/com/fibelatti/pinboard/features/user/data/UserDataSource.kt)
  • Change-coupling hub: UserSharedPreferences.kt → UserPreferences.kt, UserRepository.kt, UserPreferencesProvider.kt, UserPreferencesViewModel.kt (app/src/main/kotlin/com/fibelatti/pinboard/core/persistence/UserSharedPreferences.kt)
  • Dependency hygiene PARTLY measured — Maven/Gradle declarations read, no dependency graph resolved
  • Documentation: no installation or build instructions (README.md)
  • Documentation: no usage examples (README.md)
  • Duplicated block (10 lines × 2) (app/src/main/kotlin/com/fibelatti/pinboard/features/linkding/data/PostsDataSourceLinkdingApi.kt)
  • Duplicated block (10 lines × 2) (app/src/main/kotlin/com/fibelatti/pinboard/features/posts/presentation/SearchBookmarksScreen.kt)
  • Duplicated block (10 lines × 2) (ui/src/main/java/com/fibelatti/ui/components/ButtonToggleGroup.kt)
  • Duplicated block (12 lines × 2) (app/src/main/kotlin/com/fibelatti/pinboard/features/posts/data/PostsDataSourceNoApi.kt)
  • Duplicated block (12 lines × 3) (app/src/main/kotlin/com/fibelatti/pinboard/features/linkding/data/PostsDataSourceLinkdingApi.kt)
  • Duplicated block (14 lines × 2) (app/src/main/kotlin/com/fibelatti/pinboard/features/posts/presentation/BookmarkDetailsScreen.kt)
  • Duplicated block (15 lines × 5) (app/src/main/kotlin/com/fibelatti/pinboard/features/user/presentation/AccountSwitcherScreen.kt)
  • Duplicated block (17 lines × 2) (app/src/main/kotlin/com/fibelatti/pinboard/features/linkding/data/BookmarksDao.kt)
  • Duplicated block (19 lines × 2) (app/src/main/kotlin/com/fibelatti/pinboard/features/linkding/data/BookmarksDao.kt)
  • Duplicated block (20 lines × 2) (app/src/main/kotlin/com/fibelatti/pinboard/features/offline/presentation/OfflineCopyWebView.kt)
  • Duplicated block (23 lines × 2) (app/src/main/kotlin/com/fibelatti/pinboard/features/posts/presentation/BookmarkDetailsScreen.kt)
  • Duplicated block (24 lines × 2) (app/src/main/kotlin/com/fibelatti/pinboard/features/posts/data/PostsDataSourceNoApi.kt)
  • …and 71 more

Changes since last survey

  • 90 commits — 84 feature/other, 6 fixes

By area

  • app/src — 48 commits
  • gradle/libs.versions.toml — 27 commits
  • app/build.gradle.kts — 5 commits
  • ui/src — 5 commits
  • build-logic/convention — 2 commits
  • gradle/wrapper — 2 commits
  • core/build.gradle.kts — 1 commit

Notable commits

  • fix: Fix Private/Read Later editor alignment
  • fix: Fix ListItem contrast
  • fix: Fix TagManagerDataSource flow collection leak
  • fix: Fix instrumented tests references
  • fix: Fix offline copy bottom bar overlap
  • fix: Fix radio button contrast
  • change: Add CA exception handling
  • change: Add ConnectedButtonRowItem
  • change: Add SuppressLint to WebViewClient
  • change: Add bulk delete for offline copies
  • change: Add export option to the login screen
  • change: Add missing capitalization to bookmark fields
  • change: Add top inset to AppBottomSheet
  • change: Attempt to make Pinboard models a bit more lenient
  • change: Build raw queries with RoomRawQuery
  • change: Build the export document with createHTML
  • change: Bump aboutLibraries from 15.0.4 to 15.1.1 (#726)
  • change: Bump aboutLibraries from 15.1.1 to 15.2.0 (#735)
  • change: Bump adaptive-android to 1.3.0
  • change: Bump agp from 9.3.1 to 9.3.2 (#727)
  • …and 70 more

Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.

Survey your own repository

fibelatti/pinboard-kotlin was measured the same way every project in this corpus was: the same rubric, at a pinned commit, with the result published in full. Point a surveyor at a repository you know and see whether you agree with it.

About this page

  • The score is its most recent published measurement, taken on 22 September 2026 at a pinned commit. It is not a live figure and does not change until the project is measured again.
  • Measured at commit 718705c49f48e5dc4126f2b79ca7ea81e1ac44a5 — the exact code this score is about.
  • Scored under rubric-2026.09.15 — the same rubric and the same method as every other entry in this index.
  • Measured by watchdog.canine.dev using codehealth-analyzer preprod-821afab8930d.