A premium AniList client for Android & Desktop built with Wails 3
Miku is a feature-rich AniList client that delivers a premium anime and manga tracking experience on Android and Desktop. Built with Wails 3, it combines a Go backend for OAuth2 authentication with a Vue 3 frontend featuring a custom anime-inspired design language with cherry blossom motifs and switchable light/dark/black themes.
- OAuth2 Authentication — Secure login via AniList's OAuth2 flow with localhost callback (works on desktop and Android)
- Trending & Discovery — Browse trending, popular, seasonal anime, and top manga
- Advanced Search — Full-text search with anime/manga/character/staff type filters
- Personal Lists — Manage your anime lists (Watching, Planning, Completed, Dropped, Paused)
- List Management — Add/update anime to your list directly from detail pages (status, score, progress)
- Activity Feed — Following-style feed showing your own activity plus the activity of everyone you follow on AniList (like the AniList site). Post status updates with the built-in composer; activity text renders as rich markdown. Tap a friend's avatar to open their AniList profile
- Profile & Stats — View your profile with detailed statistics and breakdowns (genres, tags, studios, staff, voice actors), favourites (anime, manga, characters, staff), following/followers, paginated activity history, and an activity heatmap
- Update Notifications — Bell icon on Discover page checks GitHub Releases for new versions
- Download APK — Download updates directly in the app with real-time progress (speed, file size)
- Auto-Install — Opens the system installer after download
- Version Display — Profile page shows current version with update indicator
- Update Settings Card — Check the installed version and latest release directly from Settings
- Episode Notifications — The Discover notification bell alerts you when a new episode is available for anime in your Watching list
- Individual Read Controls — Mark an individual update or episode notification as read
- Mark All as Read — Clear all update and episode notifications from the bell at once
- Persistent Episode Read State — Acknowledged episode releases do not repeatedly notify the user
- Character Search — Search by character name to find anime/manga
- Staff Search — Search by voice actor/staff name to find their works
- Multi-Type Filters — Switch between Anime, Manga, Characters, and Staff search
- Media Details — Full anime/manga detail pages with banner, cover, scores, genres, description, airing info
- Characters & Voice Actors — View cast with character images and Japanese voice actors
- Multiple Voice Actors — Shows all voice actors per character with "(child)" tag for child versions
- Voice Actor Pages — Click any voice actor to see all their voiced roles across anime (uses correct AniList API)
- Relations — Navigate between related anime (prequels, sequels, adaptations)
- Recommendations — Discover similar anime (deduplicated)
- Real-time List Sync — Your anime list auto-syncs with AniList every 60 seconds
- Activity Refresh — Feed updates automatically when you switch tabs
- Theme Modes — Dark (default), Light, Black (AMOLED), and System (follows OS) via design-token overrides
- Accent Color Picker — Preset swatches plus a custom color chooser that re-tints the whole UI
- Cherry Blossom Motifs — Unique branding with sakura-inspired animations
- Smooth Animations — Page transitions, skeleton loading, micro-interactions
- Virtual Scrolling — Optimized list rendering for smooth 60 FPS performance
- Mobile-First Design — Optimized for Android and desktop
- Pull-to-Refresh — Touch-based on mobile, manual button on desktop; available on Discover, My List, and Feed. Direction-locked to prevent conflicts with horizontal pill filters
- Title Language — Choose romaji / English / native titles, or follow your AniList account setting
- Score Format — 100-point, 10-point, 10-point decimal, 5-star, or 3-point display
- Default Tab — Start the app on Discover, Search, My List, Feed, or Profile
- Adult Content Toggle — Filter adult titles in browse/search, defaulting to your AniList account setting
- Recent Activity Toggle — Show or hide the recent activity feed on your profile (off by default)
- Stats Breakdown Toggles — Individually show or hide Top Genres, Top Tags, Top Studios, Top Staff, and Top Voice Actors sections (all on by default)
- Collapsible Stats on Mobile — Stats breakdown sections (genres, tags, studios, staff, voice actors) use collapsible dropdown headers on mobile for a cleaner view; desktop keeps them always expanded
- GraphQL API — Efficient data fetching with AniList's GraphQL API
- Type Safety — Full TypeScript coverage with strict mode
- State Management — Pinia stores for reactive state handling
- Cross-Platform — Works on Windows, macOS, Linux, and Android
- Testing — Go suites with testify (unit, integration via httptest, regression, chaos) and Vitest frontend suites; run everything with
wails3 task test
| Layer | Technology | Purpose |
|---|---|---|
| Desktop/Mobile Framework | Wails 3 | Cross-platform native apps with Go |
| Frontend | Vue 3 + TypeScript | UI rendering and state management |
| Build Tool | Vite | Fast development and production builds |
| State Management | Pinia | Reactive state stores |
| Routing | Vue Router | Client-side navigation |
| Backend | Go 1.25 | OAuth2 localhost server, update service, business logic |
| API | AniList GraphQL | Anime/manga data (via frontend fetch) |
| Testing | Testify + Vitest | Go assertions + frontend unit/integration suites |
- Go 1.25 or later — install guide
- Node.js 18+ and npm — install guide
- Wails 3 CLI —
go install github.com/wailsapp/wails/v3/cmd/wails3@latest - Android SDK (for Android builds) — install guide
git clone https://github.com/Aswanidev-vs/Miku.git
cd Mikucd frontend
npm install
cd ..Create a .env file in the project root:
ANILIST_CLIENT_ID=your-client-id
ANILIST_CLIENT_SECRET=your-client-secretSee AniList API Setup below for detailed instructions.
wails3 devThis starts both the Go backend and the Vite dev server with hot-reload.
wails3 buildThe compiled binary will be placed in the build directory.
- Windows 10 or later
- Inno Setup 6
Build the Windows executable and installer with:
wails3 task windows:package FORMAT=inno ARCH=amd64The installer is written to bin/Miku--windows-setup.exe. It opens the normal Inno Setup directory picker and defaults to C:\Program Files\Miku. Users can choose another drive and folder, such as E:\Miku; the selected Miku directory is created automatically and the application is installed there. The installer also handles the Microsoft Edge WebView2 Runtime and creates Start Menu and optional desktop shortcuts.
FORMAT=nsis remains available for the existing NSIS package, and FORMAT=msix creates an MSIX package.
- Android SDK (API level 33+)
- Java Development Kit (JDK 17+)
- Android NDK
- The app includes a
network_security_config.xmlthat allows cleartext HTTP tolocalhostfor the OAuth callback - OAuth on Android uses Chrome Custom Tabs redirecting to
http://localhost:43219/callback(there is no custom-scheme deep link — Chrome Custom Tabs cannot navigate to custom-scheme URLs) - Debug builds keep the
x86_64ABI so the app also runs in the Android Emulator; release/device APKs arearm64-v8aonly - APK updates downloaded from GitHub use Android's system package installer. On the first update, Android may ask you to allow Miku to install unknown apps; after that setting is enabled, the updater resumes automatically.
The installed size is dominated by the Go libwails.so native library, so release builds are tuned to minimize it:
- arm64-v8a only for release —
x86_64is restricted to debug builds. Every real device is arm64, so release APKs stop shipping the redundant x86_64.so. - Stripped symbols by default — Android builds run with
PRODUCTION=true, linking Go with-ldflags="-w -s"(no debug info), shrinkinglibwails.soby roughly 40–50%. - R8 shrinking without obfuscation — Release builds enable R8 minification for dead-code removal while keeping
-dontobfuscate, so the Go↔Java JNI bridge methods are never renamed.-dontwarnrules cover annotation classes (javax.annotation.*, checkerframework, errorprone) pulled in transitively byandroidx.security-crypto(Tink/Guava) that aren't present at runtime, keeping the release build green.
# Generate Wails bindings
wails3 generate bindings
# Build APK
wails3 task android:build
# Build and run on emulator
wails3 task android:run- Debug APK:
build/android/app/build/outputs/apk/debug/app-debug.apk - Release APK:
build/android/app/build/outputs/apk/release/app-release.apk(also copied tobin/miku.apk)
The Android application ID is intentionally kept as com.wails.app. Android
can install a new APK over an existing installation only when the application
ID and signing certificate match, and the new versionCode is higher. Release
version metadata is read from main.go; for example, 0.12.0 produces a
versionCode of 12000.
Never publish a release APK signed with a debug key. Debug keystores are machine-specific, so a new CI runner would make the same package look like a different app. Release builds require these repository secrets:
ANDROID_KEYSTORE_BASE64— base64-encoded copy of the persistent release keystoreANDROID_KEYSTORE_PASSWORDANDROID_KEY_ALIASANDROID_KEY_PASSWORD
Create the keystore once, store it securely, and add its base64 output as a GitHub Actions secret. Do not commit the keystore or its passwords:
keytool -genkeypair -v -keystore miku-release.keystore \
-alias miku-release -keyalg RSA -keysize 4096 -validity 10000
base64 -w 0 miku-release.keystoreAfter this setup, published APKs remain installable as updates without users uninstalling the previous version. An APK already installed with an unknown, different signing key cannot be repaired by Java, XML, or the installer intent; Android requires a one-time uninstall unless the original private key can be recovered and used for the next release.
Miku uses the AniList GraphQL API with OAuth2 authentication. The app runs a local HTTP server on port 43219 to receive the OAuth callback — this works on both desktop and Android (Chrome Custom Tabs redirect to http://localhost:43219/callback on the device).
- Go to anilist.co/settings/developer
- Click "Create New Client"
- Fill in the details:
- Name:
Miku(or anything you prefer) - Redirect URL:
http://localhost:43219/callback
- Name:
- Click "Create"
- Copy the generated Client ID and Client Secret
Windows (PowerShell):
$env:ANILIST_CLIENT_ID = "your-client-id"
$env:ANILIST_CLIENT_SECRET = "your-client-secret"Windows (Command Prompt):
set ANILIST_CLIENT_ID=your-client-id
set ANILIST_CLIENT_SECRET=your-client-secretmacOS / Linux:
export ANILIST_CLIENT_ID="your-client-id"
export ANILIST_CLIENT_SECRET="your-client-secret"Persistent (.env file):
Create a .env file in the project root:
ANILIST_CLIENT_ID=your-client-id
ANILIST_CLIENT_SECRET=your-client-secretNote: Add
.envto.gitignoreto avoid committing credentials.
Miku/
├── backend/
│ ├── api/
│ │ ├── client.go # GraphQL client implementation
│ │ ├── queries.go # AniList query definitions
│ │ └── mutations.go # AniList mutation definitions
│ ├── auth/
│ │ ├── oauth2.go # OAuth2 authentication service
│ │ └── token_store.go # Secure token persistence
│ ├── update/
│ │ ├── update.go # In-app update service (GitHub Releases)
│ │ └── update_test.go # Update service tests
│ └── platform/
│ └── platform.go # OS/arch detection service
├── frontend/
│ ├── src/
│ │ ├── api/
│ │ │ └── graphql.ts # GraphQL client with cache
│ │ ├── components/
│ │ │ ├── anime/
│ │ │ │ ├── AnimeCard.vue # Anime card component
│ │ │ │ └── AnimeGrid.vue # Virtualized grid
│ │ │ ├── layout/
│ │ │ │ ├── BottomNav.vue # Bottom navigation
│ │ │ │ └── UpdateNotification.vue # Update bell & panel
│ │ │ ├── profile/
│ │ │ │ ├── HeatmapCalendar.vue # Activity heatmap
│ │ │ │ ├── StatsCard.vue # User statistics
│ │ │ │ ├── StatsBreakdown.vue # Genre/tag/studio/staff/VA breakdowns
│ │ │ │ ├── SocialList.vue # Following / followers lists
│ │ │ │ └── UserFavorites.vue # Favorite anime/manga/characters/staff
│ │ │ ├── feed/
│ │ │ │ ├── ActivityComposer.vue # Post new status updates
│ │ │ │ └── ActivityItem.vue # Shared activity row (feed + history)
│ │ │ ├── settings/
│ │ │ │ ├── AppearanceSettings.vue # Theme & accent picker
│ │ │ │ └── AniListPreferences.vue # AniList account preferences
│ │ │ └── common/
│ │ │ ├── SkeletonLoader.vue # Loading skeleton
│ │ │ └── PullToRefresh.vue # Pull-to-refresh component
│ │ ├── composables/
│ │ │ ├── usePlatform.ts # Platform detection
│ │ │ ├── useSettings.ts # App settings (theme, accent, preferences)
│ │ │ ├── useUpdate.ts # Update state management
│ │ │ └── usePullToRefresh.ts # Pull-to-refresh logic
│ │ ├── utils/
│ │ │ ├── activityHtml.ts # DOMPurify-sanitized activity rendering
│ │ │ ├── activityFormat.ts # Relative time + status labels
│ │ │ └── mediaDisplay.ts # Title language + score formatting
│ │ ├── views/
│ │ │ ├── DiscoverView.vue # Discovery (Trending, Popular, Seasonal, Top Manga)
│ │ │ ├── SearchView.vue # Search with anime/manga/character/staff filters
│ │ │ ├── MyListView.vue # Personal lists with status tabs + auto-sync
│ │ │ ├── FeedView.vue # Activity feed
│ │ │ ├── ProfileView.vue # User profile with heatmap & version
│ │ │ ├── MediaDetailView.vue # Anime/manga detail with list management
│ │ │ ├── VoiceActorView.vue # Voice actor page with voiced roles
│ │ │ ├── CharacterView.vue # Character detail page
│ │ │ ├── SettingsView.vue # Redirects to profile settings
│ │ │ └── LoginView.vue # Login
│ │ ├── stores/
│ │ │ ├── auth.ts # Authentication state
│ │ │ ├── anime.ts # Anime data
│ │ │ ├── manga.ts # Manga data
│ │ │ └── user.ts # User data
│ │ ├── router/
│ │ │ └── index.ts # Route definitions
│ │ ├── types/
│ │ │ └── index.ts # TypeScript types
│ │ ├── styles/
│ │ │ ├── variables.css # Design tokens
│ │ │ ├── base.css # Base styles
│ │ │ └── animations.css # Animations
│ │ ├── App.vue # Root component
│ │ └── main.ts # Entry point
│ └── public/
│ └── logo.svg # App logo
├── build/
│ ├── android/ # Android build config
│ ├── ios/ # iOS build config
│ ├── darwin/ # macOS build config
│ ├── windows/ # Windows build config
│ └── linux/ # Linux build config
├── docs/
│ └── compose/ # Specs, plans, reports
├── main.go # Application entry point
├── go.mod # Go module definition
├── go.sum # Go dependencies
└── Taskfile.yml # Task runner config
# Development mode with hot-reload
wails3 dev
# Build for production
wails3 build
# Generate Wails bindings
wails3 generate bindings
# Build for Android
wails3 task android:build
# Run on Android emulator
wails3 task android:run
# Run all tests (Go + frontend)
wails3 task test
# Run Go tests only
go test ./... -v
# Run frontend tests only
cd frontend && npm test
# Frontend only (without Go backend)
cd frontend && npm run dev- Vue: Composition API with
<script setup lang="ts"> - TypeScript: Strict mode enabled
- CSS: Custom properties (CSS variables) for theming
- Go: Standard Go formatting with
gofmt
Follow Conventional Commits:
feat: New feature
fix: Bug fix
docs: Documentation changes
style: Code style changes (formatting, etc.)
refactor: Code refactoring
test: Adding or updating tests
chore: Maintenance tasks
This project uses GitHub Actions for automated builds. See .github/workflows/build.yml for the workflow configuration.
- Push to main: Creates a version tag; the tag build publishes the Android APK and Windows Inno Setup installer together
- Version tags: Build both Android and Windows release artifacts and attach them to the GitHub release
- Pull Request: Validates build compiles successfully
- Manual trigger: Build from Actions tab
- Themes — Dark, Light, Black (AMOLED), and System modes plus a custom accent color picker
- Activity Composer — Post status updates directly from the Feed; activity text now renders as rich markdown
- Profile Overhaul — Stats breakdowns (genres, tags, studios, staff, voice actors), character/staff favorites, Following/Followers section, About Me, and browsable activity history
- AniList Preferences — Title language, score format, default tab, and adult content settings (with account-default fallbacks)
- Feed Pull-to-Refresh — Feed tab now supports the mobile gesture + desktop refresh button
- Collapsible Stats on Mobile — Stats breakdown sections collapse/expand via dropdown headers with animated chevrons on mobile; desktop keeps them permanently expanded
- Recent Activity Toggle — New toggle in Preferences (default off) to show or hide the recent activity feed on profile; activity data is only fetched when enabled
- Stats Visibility Toggles — Individual toggles in Preferences for Top Genres, Top Tags, Top Studios, Top Staff, and Top Voice Actors (all on by default); disabling a toggle hides the corresponding section from the stats breakdown
- Pull-to-Refresh on My List — Fixed dual pull-to-refresh zones and scroll-gets-stuck bug caused by incorrect scroll container detection and missing gesture direction locking; horizontal swipes on filter pills and downward scrolling no longer accidentally trigger refresh
- Go — testify suites for the GraphQL client (integration + chaos), OAuth2 flow, token store, and platform service
- Frontend — Vitest suites (composables, components, utils, Pinia stores); run everything with
wails3 task test
- Staff Search — Search by voice actor/staff name to find their works
- Multiple Voice Actors — Shows all voice actors per character with "(child)" tag for child versions
- Fixed voice actor page not showing all voiced roles (uses correct AniList API)
- Fixed character images not loading for child voice actors
- Fixed child VA row not showing character name
- In-App Update Notifications — Bell icon on Discover page checks GitHub Releases for new versions
- Download APK Updates — Download updates directly in the app with real-time progress (speed, file size)
- Character Search — Search by character name to find anime/manga
- Version Display — Profile page shows current version with update indicator
- Go Tests — Backend tests with testify library
- Activity Heatmap — Now fetches full year of activity data
- Search UI — Added 4 filter tabs: Anime, Manga, Characters, Staff
- Mobile Responsive — Update notification bell visible on all devices
- Fixed version not displaying in ProfileView
- The Discover notification bell continues to check automatically for new app releases and shows a badge when an update is available.
- The Settings menu contains the manual Check for Updates card, which reports whether the installed version is current or an update is available.
- Anime in the user's Watching list generate a bell notification when AniList reports a newly released episode beyond the user's watched progress.
- Episode and update notifications support both individual Mark as read actions and Mark all as read.
- Initial release with core features
Contributions are welcome! Here's how to get started:
- Fork the repository
- Create a feature branch from
maingit checkout -b feature/your-feature-name
- Make your changes
- Test your changes in development mode
wails3 dev
- Run the test suites
wails3 task test - Commit with a descriptive message
git commit -m "feat: add your feature description" - Push to your fork and open a Pull Request
- Follow the Installation steps
- Create a
.envfile with your AniList credentials - Run
wails3 devto start development - Make your changes with hot-reload
- Test thoroughly before submitting
- OAuth login links still open in Chrome Custom Tabs on Android because the localhost callback flow depends on that behavior.
- Staff and voice actor external links use Android's normal app-link resolution instead. If the user has YouTube, Instagram, X/Twitter, or another matching app installed, Android can open that app directly.
- If no installed app can handle the link, Android falls back to the user's default browser.
This project is licensed under the MIT License. See the LICENSE file for details.
- AniList — For the API and platform
- Wails — For the cross-platform framework
- Vue.js — For the frontend framework
- Pinia — For state management
- Dexie.js — For IndexedDB wrapper
- Testify — For Go testing assertions
Made with ♥ by Aswanidev-vs





