Native Shell
Native Shell:AI 生成的 UI,在你的應用中原生呈現,涵蓋每個平台。iOS、Android、macOS、Windows 與 Linux 上的應用、GPUI 應用與網頁在執行時載入已簽名、沙箱隔離的 TSX 模組並繪製 th
AI 翻譯
Online Reader Framework is an open source (MIT) React library that turns a book into a searchable, adjustable web edition with notes, read-aloud, sandboxed interactive examples, EPUB export and Markdo
翻譯準備中
開發工具TypeScript · JavaScriptGitHub @techczech上線中營運者未確認
試用過了嗎? 點一下就好,不需要登入。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證MIT-licensed React reader framework for searchable web books, reading assistance, notes, EPUB and agent-readable text.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Online Reader Framework is a React library that turns a book, guide or long manual into a web edition that is comfortable to read. You give it a book as plain data (title, author, and a tree of sections written in Markdown or sanitised HTML). It supplies the reader: contents, search, reading settings, highlights, notes, bookmarks, progress, read-aloud, sandboxed interactive examples, EPUB export and Markdown files for AI agents.
Version 1.1.0 (changes). Licence: MIT for the software (see LICENSING.md for what the licence does and does not cover). Project page: https://techczech.github.io/onlinereaderframework/.
The framework carries no published book; the small example book in examples/reader-example was written for this repository. Five editions built on the framework are live: Deliberate Practice, Building Your Language Muscle, Key Issues in Language, Increasing Reading Productivity and Foundations of Readability. Their text is not in this repository and is not covered by its licence.
?section=<id>). Six front-page layouts are available per book.DOMParser and XMLSerializer). Under plain Node the generator rejects with a clear error. Images are included only if the host supplies their bytes; generation makes no network requests.Node 22.12 or later and npm. React and React DOM, version 18.3.1 or later, or 19.2.0 or later, are peer dependencies of the host.
npm run setup # installs packages/reader-core and examples/reader-example from their lockfiles, with lifecycle scripts disabled
npm run build # compiles to packages/reader-core/dist: JavaScript, declarations, reader.css and local fonts
npm test # Node tests for content, agent files, embeds, reading settings, read-aloud, listening and more
npm run test:native
npm run test:native imports the built package by name in plain Node, checks the embed functions and reader-core/embed-child, generates agent files for a small book and checks the browser-only EPUB boundary.
npm run setup passes --min-release-age=7, so npm refuses package versions published in the last seven days.
examples/reader-example is a small book with a working reader. Its text is invented demonstration text, released under the MIT licence with the code. It shows chapters inside chapters, headings, lists, a table, a quotation and a footnote, the reading settings and assists, notes, read-aloud, one interactive example with its text version, an EPUB download and agent files.
npm run build # the example uses the built package, so build it first
npm run example # builds the example into examples/reader-example/dist
npm run test:example # Node checks of the built example
npm run example:preview # serves the build at http://127.0.0.1:4173/ with its hosting headers
npm run example:no-embeds builds the same book without the interactive example, for GitHub Pages and other hosts that cannot send headers. The build is static; copy dist/ to a host. Rebuild the example after you change the package.
To make your own book, copy the folder, replace src/example-book.mjs with your book, and change or remove the embed registry at the end of that file.
Copy or link the built packages/reader-core directory into your project, for example as a local file dependency:
npm install file:../onlinereaderframework/packages/reader-core
Import the stylesheet once and mount the reader:
import { PublicationReader, type PublicationBook } from 'reader-core';
import 'reader-core/reader.css';
const book: PublicationBook = {
id: 'my-guide', // stable; namespaces every saved setting
title: 'My Guide',
author: 'A. Author',
description: 'One sentence about the book.',
sections: [
{ id: 'start', title: 'Start here', category: 'Basics', content: '# Start here\n\nSome **Markdown**.' },
{ id: 'part-1', title: 'Part one', category: 'Basics', content: 'Opening text.', children: [
{ id: 'part-1-detail', title: 'A detail', category: 'Basics', content: '<p>HTML works too.</p>', format: 'html' },
] },
],
};
export default function App() {
return <PublicationReader book={book} />;
}
Section ids must be unique across the whole tree, and home is reserved. Each section takes content and an optional format of markdown (the default) or html. PublicationBook also accepts subtitle, language, downloads, sourceUrl, labels, navigation (home and sidebar depth), appearance (accent, paper and ink colours, masthead, cover image, front-page layout) and home (badge, overview text, feature cards, author note, licence link). Anything you omit gets a default. Read packages/reader-core/src/original/types.ts for the exact contract.
coaching: optional { label, render } to mount a host-owned panel.audio: optional list of audio tracks with optional alignment, for the listening panel.epub: options for EPUB export (assets carries image bytes), or false to turn the export off.agent: { path, siteUrl } to show a link to generated agent files, or false. Off by default.embeds: a registry of interactive examples (below), or false. Off by default.The package also keeps an earlier, smaller reader. parseMarkdownToSections splits one Markdown document into block-based sections, and ReaderShell displays them:
import { ReaderShell, parseMarkdownToSections } from 'reader-core';
import 'reader-core/reader.css';
const sections = parseMarkdownToSections(markdownText, { sectionHeadingLevel: 'auto' });
export default function App() { return <ReaderShell sections={sections} />; }
It has its own section type and fewer features than PublicationReader, which is the full reader used for the live editions and the one this document describes.
Run this in Node, for example in a build script. The function is pure; you decide where to write the files.
import { buildAgentBook } from 'reader-core/agent';
import { mkdirSync, writeFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
const out = buildAgentBook(book, { siteUrl: 'https://books.example.org/reader/', directory: 'agent' });
for (const file of out.files) {
const target = join('dist', file.path);
mkdirSync(dirname(target), { recursive: true });
writeFileSync(target, file.content);
}
siteUrl must be a public http(s) address; credentials, queries, fragments and private hosts are refused before any file is produced. Without it, links are relative. Markdown sections are written exactly as authored; HTML sections go through the reader's sanitiser. Nothing from a reader's browser state can reach the files.
The book's text holds the text version as one block element with an id. The host registers the interactive document for that element:
import { PublicationReader, type PublicationBook, type PublicationEmbeds } from 'reader-core';
const book: PublicationBook = {
id: 'my-guide', title: 'My Guide', author: 'A. Author', description: 'One sentence about the book.',
sections: [{ id: 'spacing', title: 'Line spacing', category: 'Reading', format: 'html',
content: '<div id="spacing-text"><p>A full written description of what the interactive example shows and what the reader can try in it.</p></div>' }],
};
const embeds: PublicationEmbeds = { root: 'embeds/', items: [
{ id: 'spacing', sectionId: 'spacing', anchor: 'spacing-text', src: 'spacing.html', title: 'Line spacing explorer' },
] };
export default function App() {
return <PublicationReader book={book} embeds={embeds} />;
}
The file embeds/spacing.html is then served from the folder of the page.
The reader places a frame before the element, with a visible title, a link to skip to the text version and a link to a full view. The frame has sandbox="allow-scripts" and nothing else, so the document runs in an opaque origin with no access to the page, storage, cookies or pop-ups. src must be a plain relative .html path under root on the page's own origin. The only message the reader accepts is the frame's content height. Run checkPublicationEmbeds(book, embeds) at build time: it reports invalid entries, missing sections or anchors, and text versions shorter than 80 characters. The text of a chapter cannot create a frame, because the sanitiser removes iframe and script.
Write each embed as one HTML file with its script and styles inline and no network requests. To let the frame follow its height, inline startReaderEmbedHeightReporting from reader-core/embed-child; the example's build.mjs shows how. The full contract is in packages/reader-core/src/embed/README.md.
The frame attribute protects the embed inside the reader, but the same file can also be opened directly. Serve the embed folder with this response header, which keeps it sandboxed when opened directly and blocks network access:
Content-Security-Policy: sandbox allow-scripts; default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data:; font-src data:; media-src data:; connect-src 'none'; form-action 'none'; base-uri 'none'; frame-ancestors 'self'
_headers file with this rule, the stricter policy for the reader pages, and ! lines that remove the reader policy from the embed folder. The live Increasing Reading Productivity and Foundations of Readability editions are served on Cloudflare Pages with the same policies. The example itself was checked with the local preview, not on Cloudflare.examples/reader-example/hosting-headers.mjs lists them, and serve.mjs shows them applied._headers but not the ! lines, so it needs its own rule._headers. Use npm run example:no-embeds, or leave embeds out of your own build, so the site holds no interactive documents and every chapter shows its text version.import { downloadPublicationEpub } from 'reader-core';
await downloadPublicationEpub(book, { assets: [] }); // browser only
renderPublicationContent, publicationContentToText, getPublicationHeadings, generatePublicationEpub, publicationDisplayTitle, FRONT_PAGE_LAYOUTS, ListeningPanel, ReadingSupportControls, exportHighlightsToMarkdown, buildLlmsText, and for embeds checkPublicationEmbeds, validatePublicationEmbeds, resolvePublicationEmbedUrl, readPublicationEmbedHeight, PublicationEmbedFrame, PublicationEmbedSlots and PUBLICATION_EMBED_SANDBOX. reader-core/embed-child exports startReaderEmbedHeightReporting and postReaderEmbedHeight for use inside an embed document. Read packages/reader-core/src/index.ts.
packages/reader-core: the library, its lockfile and the font licence notices.examples/reader-example: the example book, its build, preview server and hosting headers.scripts: the build scripts for JavaScript, declarations, the stylesheet and fonts, and the native-import test.docs: the static project page, published with GitHub Pages from the docs folder of main.provenance-reader-source.json: where reused parts come from, and the SHA-256 of every exported file.CHANGELOG.md: what changed in each release.The reader is the author's own work, assembled from his earlier applications: the interface of the Deliberate Practice guide, the recursive navigation and EPUB construction of the Building Your Language Muscle guide, the speech contract of ReadFlex and the anchored-reading rule of WriteFlex. provenance-reader-source.json records the original file names and hashes. The reading design also draws on the author's readability guidance: Foundations of Readability, Increasing Reading Productivity (the Academic Reading Framework) and Principles of Readability.
Software: MIT, copyright 2026 Dominik Lukeš. Example book text: MIT, like the code. Fonts: separate open licences, reproduced in packages/reader-core/notices. Published books: not included, and not covered. See LICENSING.md.