| name | Paperand Development |
| description | Guide for developing and maintaining the Paperand React Native manga reader application |
Paperand Development Skill
This skill provides comprehensive guidance for working with the Paperand project - an ad-free manga reader built with Expo and React Native, compatible with Paperback extensions.
Project Overview
Paperand is a cross-platform manga reader app that:
- Provides an elegant, ad-free manga reading experience
- Is compatible with Paperback extension sources
- Supports both Android and iOS platforms
- Features a personal library, reading progress tracking, and multi-source search
Tech Stack
| Technology | Purpose |
|---|
| Expo SDK 54 | Development framework |
| React Native 0.81 | Cross-platform mobile development |
| TypeScript | Type-safe JavaScript |
| React Navigation 7 | Navigation library (stack + bottom tabs) |
| AsyncStorage | Persistent local storage |
| expo-image | Optimized image loading with caching |
| WebView | Extension runtime for Paperback sources |
| i18n-js | Internationalization |
Project Structure
paperback-android/
├── App.tsx # Main app entry, providers setup
├── index.ts # Expo entry point
├── app.json # Expo configuration
├── package.json # Dependencies
├── src/
│ ├── components/ # Reusable UI components
│ │ ├── MangaCard.tsx # Manga cover card component
│ │ ├── ChapterListItem.tsx # Chapter list item
│ │ ├── ExtensionRunner.tsx # WebView extension runtime
│ │ ├── PickerModal.tsx # Custom picker modal
│ │ └── ...
│ ├── context/ # React Context providers
│ │ ├── ThemeContext.tsx # Theme management
│ │ └── LibraryContext.tsx # Library & progress state
│ ├── navigation/ # Navigation configuration
│ │ ├── AppNavigator.tsx # Main stack navigator
│ │ └── BottomTabNavigator.tsx # Bottom tab navigation
│ ├── screens/ # App screens
│ │ ├── LibraryScreen.tsx # User library
│ │ ├── DiscoverScreen.tsx # Browse sources
│ │ ├── SearchScreen.tsx # Multi-source search
│ │ ├── MangaDetailScreen.tsx # Manga details & chapters
│ │ ├── ReaderScreen.tsx # Manga reader (core feature)
│ │ ├── DownloadManagerScreen.tsx # Download management
│ │ ├── ExtensionsScreen.tsx # Manage extensions
│ │ └── ...
│ ├── services/ # Core business logic
│ │ ├── sourceService.ts # Extension API bridge
│ │ ├── extensionService.ts # Extension management
│ │ ├── cacheService.ts # Image caching
│ │ ├── downloadService.ts # Chapter downloads
│ │ ├── themeService.ts # Theme file parsing
│ │ ├── i18nService.ts # Internationalization
│ │ ├── updateService.ts # App update checks
│ │ └── ...
│ ├── hooks/ # Custom React hooks
│ ├── types/ # TypeScript type definitions
│ ├── constants/ # App constants
│ └── locales/ # Translation files (17 languages)
├── android/ # Android native code
├── assets/ # Images, icons, fonts
├── plugins/ # Custom Expo config plugins
└── credentials/ # Build credentials
Development Commands
npm install
bun install
npx expo start
npm run android
npm run ios
npm run web
Key Architectural Patterns
1. Extension System
The app uses a WebView-based extension runtime (ExtensionRunner.tsx) to execute Paperback-compatible extensions. Extensions are JavaScript bundles that provide manga source implementations.
2. Context Providers
- ThemeContext: Manages app theme (dark/light mode) and custom
.pbcolors themes
- LibraryContext: Manages user library, favorites, reading progress, and history
3. Service Layer
All business logic is encapsulated in service files:
sourceService.ts - Bridge between app and extensions
cacheService.ts - Image caching with configurable limits
downloadService.ts - Background chapter downloads
i18nService.ts - Multi-language support
4. Navigation Structure
Stack Navigator (AppNavigator)
├── Bottom Tab Navigator
│ ├── Library
│ ├── Discover
│ ├── History
│ └── More
├── MangaDetail
├── Reader
├── Search
├── Extensions
├── Settings
└── ...
Internationalization (i18n)
The app supports 17+ languages. Translation files are in src/locales/ as JSON files.
Adding a new translation:
- Create a new JSON file in
src/locales/ (e.g., de.json)
- Copy the structure from
en.json
- Translate all strings
- Register the language in
i18nService.ts
Theming
The app supports custom themes via .pbcolors files (Paperback theme format).
Theme configuration:
- System theme support (automatic dark/light mode)
- Custom color parsing in
themeService.ts
- Theme application via
ThemeContext
Building for Production
GitHub Actions (Automated)
On push to main or manual trigger:
- Builds Android APK with signing
- Builds iOS IPA
- Creates GitHub Release with artifacts
Local EAS Build
eas build --platform android --profile preview
eas build --platform ios --profile preview
Common Development Tasks
Adding a New Screen
- Create the screen component in
src/screens/
- Export it from
src/screens/index.ts
- Add the route in
src/navigation/AppNavigator.tsx
- Add navigation types if using TypeScript
Adding a New Component
- Create the component in
src/components/
- Export it from
src/components/index.ts
- Use the theme from
ThemeContext for styling
Adding a New Service
- Create the service file in
src/services/
- Use AsyncStorage for persistent data
- Follow existing patterns for async operations
Code Style Guidelines
- Use TypeScript for all new files
- Use functional components with hooks
- Use the app's theme system for colors (
useTheme())
- Use
i18n.t('key') for all user-facing strings
- Keep components focused and reusable
- Use services for business logic, not in components
Testing & Debugging
- Developer Menu: Tap version number in More > About 7 times
- Console Logging: Use standard
console.log (visible in Metro bundler)
- React Native Debugger: Connect via Expo dev tools
Important Notes
- The app uses Expo managed workflow with custom native code via config plugins
- Background downloads require foreground service configuration (Android)
- Extension execution happens in an isolated WebView context
- Image caching is handled by expo-image with custom cache management
Quick Reference
Import Patterns
import { useTheme } from '../context/ThemeContext';
import { useLibrary } from '../context/LibraryContext';
import { useNavigation, useRoute } from '@react-navigation/native';
import { MangaCard, LoadingIndicator, EmptyState } from '../components';
import { sourceService } from '../services/sourceService';
import { cacheService } from '../services/cacheService';
import { i18n } from '../services/i18nService';
Screen Template
import React from 'react';
import { View, StyleSheet } from 'react-native';
import { useTheme } from '../context/ThemeContext';
export function NewScreen() {
const { colors } = useTheme();
return (
<View style={[styles.container, { backgroundColor: colors.background }]}>
{/* Content */}
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
});
Navigation Params
navigation.navigate('MangaDetail', { manga, sourceId });
navigation.navigate('Reader', { manga, chapter, sourceId });
const { manga, sourceId } = useRoute().params;
Styling Patterns
Theme Colors
const { colors, isDark } = useTheme();
Common Styles
{ backgroundColor: colors.card, borderRadius: 12, padding: 16 }
{ color: colors.text, fontSize: 16 }
{ color: colors.textSecondary, fontSize: 14 }
{ height: 1, backgroundColor: colors.border }
State Management
Library Context Methods
const {
library,
favorites,
readingProgress,
addToLibrary,
removeFromLibrary,
toggleFavorite,
updateProgress,
} = useLibrary();
AsyncStorage Keys
| Key | Type | Description |
|---|
@library | Manga[] | Saved manga |
@favorites | string[] | Favorite IDs |
@reading_progress | object | Reading positions |
@extensions | Extension[] | Installed extensions |
@settings | object | User settings |
@history | HistoryItem[] | Reading history |
Extension API
Source Service Methods
await sourceService.getDiscoverSection(sourceId, sectionId);
await sourceService.searchManga(sourceId, query);
await sourceService.getMangaDetails(sourceId, mangaId);
await sourceService.getChapters(sourceId, mangaId);
await sourceService.getChapterPages(sourceId, chapterId);
Error Handling
try {
const data = await sourceService.getMangaDetails(sourceId, mangaId);
} catch (error) {
console.error('Failed to load manga:', error);
}
Performance Tips
- Images: Use
expo-image with cachePolicy="memory-disk"
- Lists: Use
FlatList with keyExtractor and getItemLayout
- Memoization: Use
useMemo/useCallback for expensive operations
- Lazy Loading: Load chapters/pages on demand
File Naming
| Type | Convention | Example |
|---|
| Screen | *Screen.tsx | LibraryScreen.tsx |
| Component | PascalCase.tsx | MangaCard.tsx |
| Service | *Service.ts | cacheService.ts |
| Hook | use*.ts | useDebounce.ts |
| Context | *Context.tsx | ThemeContext.tsx |
| Types | index.ts | types/index.ts |
Troubleshooting
| Issue | Solution |
|---|
| Extension not loading | Check WebView logs, verify extension URL |
| Images not caching | Check cache size limits in settings |
| Build fails | Run npx expo prebuild --clean |
| Metro bundler stuck | Clear cache: npx expo start -c |
| Android foreground service crash | Check withBackgroundActionsServiceType plugin |
Android-Specific
- Foreground service type configured in
plugins/withBackgroundActionsServiceType.js
- Permissions in
app.json under android.permissions
- Deep links:
paperback:// and paperand:// schemes
iOS-Specific
- Background modes:
fetch enabled
- Localization: 35+ locales in
CFBundleLocalizations
- Bundle ID:
com.chiraitori.paperand.ios