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.