smithery/chiraitori

Manga Reader

Reader screen patterns and image handling

Installation

$ npx skills add smithery/chiraitori --skill manga-reader

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/chiraitori.

npx skills add smithery/chiraitori

Browse all from smithery/chiraitori

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 3,929 B
  • docs SUMMARY.md 61 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

Manga Reader

Reader Modes

Mode Description Implementation
Vertical Scroll through pages FlatList vertical
Horizontal Swipe left/right FlatList horizontal paging
Webtoon Long strip format FlatList with variable height

Reader Screen Structure

function ReaderScreen() {
  const { manga, chapter, sourceId } = useRoute().params;
  const [pages, setPages] = useState<string[]>([]);
  const [currentPage, setCurrentPage] = useState(0);
  const [readerMode, setReaderMode] = useState('vertical');

  useEffect(() => {
    loadPages();
  }, [chapter]);

  const loadPages = async () => {
    const urls = await sourceService.getChapterPages(sourceId, chapter.id);
    setPages(urls);
  };

  return (
    <View style={styles.container}>
      {readerMode === 'vertical' ? (
        <VerticalReader pages={pages} onPageChange={setCurrentPage} />
      ) : (
        <HorizontalReader pages={pages} onPageChange={setCurrentPage} />
      )}
      <ReaderOverlay 
        currentPage={currentPage} 
        totalPages={pages.length}
        chapter={chapter}
      />
    </View>
  );
}

Vertical Reader

<FlatList
  data={pages}
  keyExtractor={(_, index) => `page-${index}`}
  renderItem={({ item: url, index }) => (
    <Image
      source={{ uri: url }}
      style={styles.page}
      contentFit="contain"
      cachePolicy="memory-disk"
    />
  )}
  onViewableItemsChanged={handleViewableChange}
  viewabilityConfig={{ itemVisiblePercentThreshold: 50 }}
/>

Horizontal Reader

<FlatList
  data={pages}
  horizontal
  pagingEnabled
  showsHorizontalScrollIndicator={false}
  keyExtractor={(_, index) => `page-${index}`}
  renderItem={({ item: url }) => (
    <View style={styles.pageContainer}>
      <Image source={{ uri: url }} style={styles.fullPage} />
    </View>
  )}
  getItemLayout={(_, index) => ({
    length: screenWidth,
    offset: screenWidth * index,
    index,
  })}
  onMomentumScrollEnd={handleScrollEnd}
/>

Reading Progress

const { updateProgress } = useLibrary();

// Save progress on page change
useEffect(() => {
  updateProgress(manga.id, chapter.id, currentPage);
}, [currentPage]);

// Restore progress on load
useEffect(() => {
  const savedPage = readingProgress[manga.id]?.page || 0;
  flatListRef.current?.scrollToIndex({ index: savedPage });
}, []);

Screen Orientation

import * as ScreenOrientation from 'expo-screen-orientation';

// Lock on enter
useEffect(() => {
  ScreenOrientation.unlockAsync(); // Allow all orientations

  return () => {
    ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT);
  };
}, []);

Gesture Handling

// Tap zones for navigation
const handleTap = (x: number) => {
  const width = Dimensions.get('window').width;
  if (x < width * 0.3) {
    goToPreviousPage();
  } else if (x > width * 0.7) {
    goToNextPage();
  } else {
    toggleOverlay();
  }
};

Chapter Navigation

// Go to next chapter
const goToNextChapter = () => {
  const currentIndex = chapters.findIndex(c => c.id === chapter.id);
  if (currentIndex > 0) {
    navigation.replace('Reader', {
      manga,
      chapter: chapters[currentIndex - 1],
      sourceId,
    });
  }
};

Image Prefetching

import { Image } from 'expo-image';

// Prefetch next pages
useEffect(() => {
  const nextPages = pages.slice(currentPage + 1, currentPage + 4);
  nextPages.forEach(url => Image.prefetch(url));
}, [currentPage]);

Full Screen Mode

import * as NavigationBar from 'expo-navigation-bar';
import { StatusBar } from 'expo-status-bar';

// Hide UI
<StatusBar hidden={isFullScreen} />
await NavigationBar.setVisibilityAsync('hidden');