PubliraGitHub

可独立使用的软件库

均采用 Apache-2.0 许可证,都在 Publira 中实际使用,使用其中任何一个都无需引入整个平台。

@publira/comic-viewer

Publira 章节阅读界面所用的 React 漫画阅读器,已独立出来,界面和数据管线完全由你掌控。

  • 无头 UI 架构:组合彼此独立、支持 tree-shaking 的组件,样式全部由你自己定义。
  • 响应式跨页,根据容器宽度在单页和双页之间切换。
  • 虚拟化渲染,即使加载大量高分辨率页面,移动浏览器也不会崩溃。
  • 可插拔的数据管线:通过 definePlugin() 注入获取、解密或分析处理。
  • 日式漫画默认从右向左阅读,欧美漫画可设为从左向右,内置滑动、点击和键盘翻页。
  • 通过 resolvePage() 延迟加载页面元数据,适用于长篇文档和会过期的签名 URL。

尚未发布 1.0:在 0.x 系列中,补丁版本会尽量保持兼容,次版本则可能变更公开 API。升级前请阅读变更日志。

阅读文档
安装
npm install @publira/comic-viewer
app/reader.tsx
import * as ComicViewer from "@publira/comic-viewer";
import "@publira/comic-viewer/core.css";

export function Reader({ pages }: ReaderProps) {
  return (
    <ComicViewer.Root
      initialReadingDirection="rtl"
      pages={pages}
    >
      <ComicViewer.Viewport />
      <ComicViewer.Toolbar />
      <ComicViewer.PageNavigation />
    </ComicViewer.Root>
  );
}

publira/epub

用于解码、编码和校验 EPUB 3 的 Go 库,专为不在磁盘上读写文件的服务器设计。

  • 不依赖文件系统:Decode 只需要一个 io.ReaderAt 和归档大小。
  • 快速失败:遇到结构错误时立即返回 DecodeError。
  • 内存高效,可通过 Asset.Open 按需以流的方式读取。
  • 严格校验 EBPAJ 和 KADOKAWA 风格的命名与目录规则。
  • 固定版式的图片页面用 SVG 包裹,在任何阅读系统中都能保持页面适配。
  • 为封面、目录、正文、索引和术语表等 landmark 提供结构语义。

生成的出版物可以通过仓库附带的脚本,用 W3C 官方的 EPUBCheck 进行检查,该工具会缓存在 .tools/ 下。

阅读文档
安装
go get github.com/publira/epub
main.go
f, err := os.Open("book.epub")
if err != nil {
	log.Fatal(err)
}
defer f.Close()

st, err := f.Stat()
if err != nil {
	log.Fatal(err)
}

opt := epub.WithCompliance(epub.LevelEBPAJ)
doc, err := epub.Decode(f, st.Size(), opt)
if err != nil {
	log.Fatal(err)
}

if err := epub.Encode(out, doc); err != nil {
	log.Fatal(err)
}

publira/messageformat-dart

符合 LDML 48.2 的 Unicode MessageFormat 2.0 纯 Dart 实现,用于格式化移动客户端的消息。

  • 纯 Dart 实现,不依赖 Flutter,在服务器、命令行和 Flutter 应用中都能运行。
  • 通过 MessageFormat 工作组一致性测试套件 (LDML48.2) 的全部 461 个用例,无一跳过,并在 CI 中随每次变更运行。
  • 支持规范中标记为 Stable 的全部函数 (:string, :number, :integer, :offset, :currency, :percent),可按所有 CLDR 区域设置格式化数字。
  • 处于 Draft 阶段的日期和时间函数放在 messageformat_datetime 中,不格式化日期的应用无需打包其 CLDR 数据。
  • formatToParts 返回 sealed 类层次结构的片段,可用于生成带样式的文本等输出。
  • 格式化出错时不会抛出异常,而是将错误报告给回调并使用后备值;除默认函数外,还可以注册自定义函数。

尚未发布 1.0。遵循 LDML 48.2 (UTS #35 Part 9, tr35-78) 和 Unicode CLDR 48.2 数据,升级到更新版本的规范时会作为单独的版本发布。

阅读文档
安装
dart pub add messageformat
main.dart
import 'package:messageformat/messageformat.dart';

final mf = MessageFormat(
  'en',
  r'''
.input {$count :number}
.match $count
0   {{No episodes}}
one {{{$count} episode}}
*   {{{$count} episodes}}
''',
  options: const MessageFormatOptions(
    bidiIsolation: BidiIsolation.none,
  ),
);

mf.format({'count': 0}); // 'No episodes'
mf.format({'count': 1}); // '1 episode'
mf.format({'count': 1000}); // '1,000 episodes'