diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..12c4417 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,61 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + lint: + name: Type Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + - run: npm ci + - run: npm run lint + + build: + name: Build + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [18, 20, 22] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: npm + - run: npm ci + - run: npm run build + - name: Verify dist output + run: | + test -f dist/index.js + test -f dist/index.cjs + test -f dist/index.d.ts + test -f dist/react.js + test -f dist/react.cjs + test -f dist/react.d.ts + + test: + name: Test + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [18, 20, 22] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: npm + - run: npm ci + - run: npm test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..97dd019 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,31 @@ +name: Release + +on: + push: + tags: + - 'v*' + +permissions: + contents: read + +jobs: + publish: + name: Publish to npm + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + registry-url: https://registry.npmjs.org + - run: npm ci + - run: npm run lint + - run: npm test + - run: npm run build + - run: npm publish --provenance --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/README.md b/README.md index 10545f5..31152ca 100644 --- a/README.md +++ b/README.md @@ -1,56 +1,187 @@ # @readmigo/reader-engine -A CSS column-based pagination engine for rendering book chapter HTML in web environments. +[![CI](https://github.com/readmigo/reader-engine/actions/workflows/ci.yml/badge.svg)](https://github.com/readmigo/reader-engine/actions/workflows/ci.yml) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE) +[![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue.svg)](https://www.typescriptlang.org/) +[![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18-green.svg)](https://nodejs.org/) + +A CSS column-based pagination engine for rendering book chapter HTML in web environments. Built with TypeScript, ships as ESM and CJS with full type declarations. + +--- ## Features -- **HTML Pagination** - CSS column layout engine that splits chapter HTML into discrete pages -- **Scroll Mode** - Alternative continuous scroll reading mode with progress tracking -- **Typography Settings** - Configurable font size, family, line height, letter spacing, word spacing, paragraph spacing, text alignment, and hyphenation -- **Theme System** - Four built-in themes: Light, Sepia, Dark, and Ultra Dark -- **Chapter Navigation** - Chapter manager with ordered traversal, jump-to-chapter, and boundary detection -- **Reading Progress** - Combined chapter + page progress calculation (0 to 1) -- **Content Security** - DOMPurify-based HTML sanitization before rendering -- **React Wrapper** - Provider, View component, and hooks for React integration -- **Dual Format** - Ships as ESM and CJS with full TypeScript declarations +| Feature | Description | +|---------|-------------| +| 📖 **HTML Pagination** | CSS column layout engine that splits chapter HTML into discrete pages | +| 📜 **Scroll Mode** | Alternative continuous scroll reading mode with progress tracking | +| 🔤 **Typography Settings** | Configurable font size, family, line height, letter/word spacing, paragraph spacing, text alignment, and hyphenation | +| 🎨 **Theme System** | Four built-in themes — Light, Sepia, Dark, and Ultra Dark | +| 🧭 **Chapter Navigation** | Chapter manager with ordered traversal, jump-to-chapter, and boundary detection | +| 📊 **Reading Progress** | Combined chapter + page progress calculation (0 to 1) | +| 🔒 **Content Security** | DOMPurify-based HTML sanitization before rendering | +| ⚛️ **React Wrapper** | Provider, View component, and hooks for React integration | +| 📦 **Dual Format** | Ships as ESM and CJS with full TypeScript declarations | ## Installation -``` +```bash npm install @readmigo/reader-engine ``` -## Architecture +For React integration: + +```bash +npm install @readmigo/reader-engine react react-dom +``` + +## Quick Start -See [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md) for module diagrams and data flow. +### Vanilla TypeScript -## Design Document +```typescript +import { ReaderEngine } from '@readmigo/reader-engine'; -See [docs/DESIGN.md](./docs/DESIGN.md) for detailed design documentation covering architecture decisions, core mechanisms, data flow, security, and extensibility. +const engine = new ReaderEngine({ + apiBaseUrl: 'https://api.readmigo.com', + settings: { theme: 'sepia', fontSize: 20 }, +}); -## API Reference +// Mount to a DOM container +engine.mount(document.getElementById('reader')!); -See [docs/API.md](./docs/API.md) for complete type and method documentation. +// Load a book and its first chapter +const book = await engine.loadBook('book-123'); +await engine.loadChapter(0); -## Getting Started +// Navigate pages +engine.nextPage(); +engine.prevPage(); -See [docs/GETTING-STARTED.md](./docs/GETTING-STARTED.md) for setup and integration guides. +// Listen for state changes +engine.callbacks.onStateChange = (state) => { + console.log(`Page ${state.currentPage + 1}/${state.totalPages}`); +}; +``` + +### React + +```tsx +import React, { useEffect } from 'react'; +import { ReaderProvider, ReaderView, useReader } from '@readmigo/reader-engine/react'; + +function App() { + return ( + + + + ); +} + +function ReaderPage({ bookId }: { bookId: string }) { + const { state, loadBook, loadChapter, nextPage, prevPage } = useReader(); + + useEffect(() => { + loadBook(bookId).then(() => loadChapter(0)); + }, [bookId, loadBook, loadChapter]); + + return ( +
+ +

Progress: {(state.overallProgress * 100).toFixed(1)}%

+
+ ); +} +``` + +## Architecture + +``` +@readmigo/reader-engine +├── types/ # Data models, settings, themes +├── api/ # HTTP client + content loader +├── renderer/ # CSS generation + DOMPurify HTML rendering +├── core/ # Paginator (CSS columns) + scroll mode +├── navigation/ # Chapter traversal + progress calculation +├── engine.ts # ReaderEngine facade +└── react/ # Provider, View component, hooks +``` -## Usage Examples +The engine uses CSS multi-column layout to split content into pages, then translates the content horizontally to display one page at a time. See [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md) for detailed module diagrams and data flow. -- `examples/basic-usage.ts` - Vanilla TypeScript usage -- `examples/react-usage.tsx` - React component usage +### Theme Color Palette + +| Theme | Background | Text | Best For | +|-------|-----------|------|----------| +| Light | `#FFFFFF` | `#1A1A1A` | Daytime reading | +| Sepia | `#F4ECD8` | `#5B4636` | Reduced eye strain | +| Dark | `#1C1C1E` | `#E5E5E7` | Low-light environments | +| Ultra Dark | `#000000` | `#E5E5E7` | OLED screens / night | + +## Documentation + +| Document | Description | +|----------|-------------| +| [Architecture](./docs/ARCHITECTURE.md) | Module diagrams, data flow, CSS column pagination | +| [Design](./docs/DESIGN.md) | Architecture decisions, core mechanisms, data flow, security, and extensibility | +| [API Reference](./docs/API.md) | Complete type and method documentation | +| [Getting Started](./docs/GETTING-STARTED.md) | Setup, integration guides, and customization | + +## Examples + +| Example | Description | +|---------|-------------| +| [`examples/basic-usage.ts`](./examples/basic-usage.ts) | Vanilla TypeScript — engine lifecycle, navigation, settings | +| [`examples/react-usage.tsx`](./examples/react-usage.tsx) | React — provider, view, hooks, theme switcher | ## Development +### Prerequisites + +- Node.js ≥ 18 +- npm ≥ 9 + +### Commands + | Command | Description | |---------|-------------| +| `npm install` | Install dependencies | | `npm run build` | Build with tsup (ESM + CJS + DTS) | | `npm run dev` | Watch mode build | | `npm test` | Run tests with Vitest | | `npm run test:watch` | Watch mode tests | -| `npm run lint` | Type check with tsc | +| `npm run lint` | Type check with `tsc --noEmit` | + +### Project Structure + +``` +reader-engine/ +├── src/ +│ ├── types/ # Book, Chapter, Settings, Theme types +│ ├── api/ # ApiClient, ContentLoader +│ ├── renderer/ # ChapterRenderer, generateReaderCSS +│ ├── core/ # Paginator, ScrollMode +│ ├── navigation/ # ChapterManager, calculateOverallProgress +│ ├── react/ # ReaderProvider, ReaderView, hooks +│ ├── engine.ts # ReaderEngine facade +│ └── index.ts # Public exports +├── docs/ # Architecture, API, Getting Started +├── examples/ # Usage examples +├── tsconfig.json # TypeScript config +├── tsup.config.ts # Build config (ESM + CJS + DTS) +└── vitest.config.ts # Test config (happy-dom) +``` + +## Contributing + +1. Fork the repository +2. Create a feature branch (`git checkout -b feature/my-feature`) +3. Make your changes and add tests +4. Ensure all checks pass: `npm run lint && npm run build && npm test` +5. Commit your changes (`git commit -m 'feat: add my feature'`) +6. Push to the branch (`git push origin feature/my-feature`) +7. Open a Pull Request ## License -MIT +[MIT](./LICENSE) diff --git a/package.json b/package.json index 6f3d765..3efad73 100644 --- a/package.json +++ b/package.json @@ -8,14 +8,14 @@ "types": "./dist/index.d.ts", "exports": { ".": { + "types": "./dist/index.d.ts", "import": "./dist/index.js", - "require": "./dist/index.cjs", - "types": "./dist/index.d.ts" + "require": "./dist/index.cjs" }, "./react": { + "types": "./dist/react.d.ts", "import": "./dist/react.js", - "require": "./dist/react.cjs", - "types": "./dist/react.d.ts" + "require": "./dist/react.cjs" } }, "files": [