Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions apps/pdftools/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ page fits the canvas, up to 100%.
| --- | --- | --- |
| Home | Recent PDFs as cards with their first page, pages, size and when they were opened; Open a PDF from this device (up to 64 MB); remove a PDF (asks once more); an empty state on a first run | `pdf.open`, `pdf.info`, `pdf.page`, `pdf.close` |
| Read | Pages render one at a time at 72 dpi x zoom x 2 and are kept; thumbnails fill in one by one; the outline jumps to a section | `pdf.open`, `pdf.page` |
| Read mode | The pill's full-screen button: the pages alone. The tabs, the mode bar, the rail and both panels, the tool row and the status line give the pages their room, and the pill stays with previous and next, the page, the zoom, fit width and fit page, and Exit read mode. A quiet button at the top right leaves too (a script gets no key events, so there is no Esc). The page and the zoom you chose carry across entering and leaving; a fitted or automatic zoom fits the new canvas. A tap on a page only chooses it | `pdf.page` |
| Read mode | The pill's full-screen button: the pages alone. The tabs, the mode bar, the rail and both panels, the tool row and the status line give the pages their room, and the pill stays with previous and next, the page, the zoom, fit width and fit page, and Exit read mode. In a desktop shell the same tap puts the window in full screen too (`window.fullscreen`): the pages fill the screen, with no bar, title bar or dock, and no screen edge brings them back. There Esc, the shell's key (a script gets no key events), leaves Read mode, and so do ⌘F and another window taking the focus (`on_app_fullscreen`). A quiet button at the top right leaves too. Where the shell can't (a tap it doesn't count, or a host with no window service, such as card-host), Read mode fills the window, without a word. The page and the zoom you chose carry across entering and leaving; a fitted or automatic zoom fits the new canvas. A tap on a page only chooses it | `pdf.page` |
| Find | Search in document, the one way to search, then Return: "12 matches on 7 pages" in the left panel, the results grouped by page with the match in bold, every match highlighted on its page, the current one stronger, previous and next. Done, the results' cross or the field's cross ends the search, and the left panel shows what it did before (Pages if it showed nothing known); a change to the document ends it the same way | `pdf.find` |
| Comment | Highlight, Underline or Strike out the line you tap (tap twice for its paragraph), Note and Text box where you tap, in one of four colours; threads with replies, a status, delete. Comments carry the name kept under "Comment as" | `pdf.lines`, `pdf.comments`, `pdf.comment` |
| Fill & Sign | The form's fields outlined (required empty ones in red) and listed with their values; choose one to type its value; Add text, Add date and Add initials place a mark where you tap; initials are kept | `pdf.fields`, `pdf.fill`, `pdf.fill_sign` |
Expand Down Expand Up @@ -247,7 +247,7 @@ exceeded` in its log); `--budget-ms 1000` raises the hosts' budget

| Run | What it covers |
| --- | --- |
| `shell` | Every mode on the samples in the shell's own window, light, then dark through the shell's own style menu (the dark pass works on what the light pass left): Home and its covers, the status line against the storage's own bytes, "Open a PDF from this device" refused (on Linux with no dialog helper, "Not available here"), reading and thumbnails, Read mode (entered from the pill, a page turned and the zoom changed in it, left by Exit read mode or the corner control on that page at that zoom), find ("9 matches on 4 pages") from the top bar alone, with the left panel given back when it ends, a highlight and its reply dated on this machine's clock ("Today 19:42"), a rotation and its undo, combine with a page range ("One PDF of 5 pages"), initials placed by Fill & Sign, a text edit with an alignment and a colour, save, and the damaged file; then one cover per PDF in the cover cache, each in `library.json` |
| `shell` | Every mode on the samples in the shell's own window, light, then dark through the shell's own style menu (the dark pass works on what the light pass left): Home and its covers, the status line against the storage's own bytes, "Open a PDF from this device" refused (on Linux with no dialog helper, "Not available here"), reading and thumbnails, Read mode (entered from the pill, the window in full screen while it lasts, a page turned and the zoom changed in it, left by Exit read mode or the corner control on that page at that zoom, with the window back; then left by Esc), find ("9 matches on 4 pages") from the top bar alone, with the left panel given back when it ends, a highlight and its reply dated on this machine's clock ("Today 19:42"), a rotation and its undo, combine with a page range ("One PDF of 5 pages"), initials placed by Fill & Sign, a text edit with an alignment and a colour, save, and the damaged file; then one cover per PDF in the cover cache, each in `library.json` |
| `restart` | The same home again: the library, its titles, the page a PDF was left at, `Imported PDF.pdf` placed as an import leaves one, every current cover kept, not drawn again, and a PDF removed from Home taking its kept cover with it |
| `full` | Storage filled to 8 KB short of its 64 MiB: the refusal on Home, then a removal, which asks once more |
| `empty` | No PDFs: the empty library and its Open button, light and dark |
Expand Down Expand Up @@ -325,16 +325,28 @@ they leave the step out and say so in `run.json`.
`--edit-sample` page: the five-pieces note, the font and room warnings with
Apply anyway, the plain refusal of Chinese text, an edit made anyway and
one made at once. Edit on macOS since this change is **unverified**.
- **Verified on 13 Oct 2026, Read mode in full screen** (the Linux build
host: Ubuntu 26.04, x86_64, a private headless weston;
[tests/evidence/read-fullscreen-20261013](tests/evidence/read-fullscreen-20261013/README.md)):
the model tests; the `shell` run with the real engine at the runtime's
64 ms budget, light and dark, on #520 at `18c11642`. The pill's
full-screen button put the window over the shell's whole window, Exit
read mode and the corner control gave it back on the same page at the
same zoom, and Esc ended both the full screen and Read mode. These are
**unverified**: the same on macOS; the OctoSense window entering the
host's full screen, which a hidden shell never asks for; and card-host,
which serves no window service, so Read mode there should fill its view
as before without a word.
- **Not verified**: the host's own file dialog (a hidden window on the Mac
cannot open it; on Linux a stand-in helper answered); with the real engine,
form fields, outlines, Redo, the other comment and page operations,
password-protected PDFs, the eight-PDF cap and the unsaved-changes question
(the dev fixture covers their screens); other screen sizes; on Linux, X11,
a GPU and a desktop compositor; Windows; phones.
- **Known**: Read mode fills the app's window, not the screen: no app can
make the OctoSense window itself full screen (there is no host API for it),
so maximize the window for the full effect. On a heavily loaded machine a
tap can overrun the 64 ms budget
- **Known**: Read mode's full screen asks the shell to put the OctoSense
window itself in full screen as well; a hidden test shell never asks the
host window, so that step on a real display is **unverified**. On a
heavily loaded machine a tap can overrun the 64 ms budget
and be lost (seen at load averages of 60 to 150 before `library.json` moved
off the tap path). The pdf service's file writes run on the UI thread and
count against that budget too (overruns after 262 and 555 ms renames, at
Expand Down
15 changes: 11 additions & 4 deletions apps/pdftools/README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ PDF Tools 以 1296 x 703 点打开,应用区域为 1292 x 667,即它的宽
| --- | --- | --- |
| 主页 | 最近的 PDF 以卡片显示首页、页数、大小和打开时间;Open a PDF from this device(最大 64 MB);移除 PDF(会再确认一次);首次运行时的空状态 | `pdf.open`、`pdf.info`、`pdf.page`、`pdf.close` |
| Read | 页面按 72 dpi × 缩放 × 2 逐页渲染并保留;缩略图逐个填入;大纲可跳转到章节 | `pdf.open`、`pdf.page` |
| 阅读模式 | 浮动控制条上的全屏按钮:只显示页面。文档标签、模式栏、侧栏和两侧面板、工具行以及状态栏都让出空间,浮动控制条保留上一页和下一页、页码、缩放、适合宽度、适合页面以及 Exit read mode。右上角一个不显眼的按钮也可以退出(脚本收不到按键事件,因此没有 Esc)。进入和退出时,当前页和你选择的缩放保持不变;适合宽度、适合页面或自动缩放会适应新的画布。在页面上点按只会选中该页 | `pdf.page` |
| 阅读模式 | 浮动控制条上的全屏按钮:只显示页面。文档标签、模式栏、侧栏和两侧面板、工具行以及状态栏都让出空间,浮动控制条保留上一页和下一页、页码、缩放、适合宽度、适合页面以及 Exit read mode。在桌面 Shell 中,同一次点按还会让窗口进入全屏(`window.fullscreen`):页面占满屏幕,没有栏、标题栏或程序坞,屏幕边缘也不会把它们唤出。此时按 Esc(这是 Shell 的按键,脚本收不到按键事件)会退出阅读模式,⌘F 以及其他窗口获得焦点时也会退出(`on_app_fullscreen`)。右上角一个不显眼的按钮也可以退出。Shell 做不到时(点按不被计入,或宿主没有窗口服务,例如 card-host),阅读模式像以前一样占满窗口,不显示任何提示。进入和退出时,当前页和你选择的缩放保持不变;适合宽度、适合页面或自动缩放会适应新的画布。在页面上点按只会选中该页 | `pdf.page` |
| 查找 | Search in document 是唯一的查找入口,输入后按 Return:左侧面板显示“12 matches on 7 pages”,结果按页分组、匹配处加粗,每处匹配在页面上高亮,当前匹配更醒目,可前后切换。按 Done、结果面板的关闭按钮或搜索框的清除按钮结束查找后,左侧面板恢复之前显示的内容(之前的内容不明时显示 Pages);文档发生更改时也以同样方式结束查找 | `pdf.find` |
| Comment | 对点按的那一行做高亮、下划线或删除线(点按两次作用于整段),在点按处添加 Note 和 Text box,四种颜色可选;带回复、状态和删除的评论线程。评论使用在“Comment as”中保存的名字 | `pdf.lines`、`pdf.comments`、`pdf.comment` |
| Fill & Sign | 用框线标出表单字段(未填的必填字段为红色)并列出其值;选中一个字段即可输入值;Add text、Add date 和 Add initials 会在点按处放置标记;姓名缩写会被保存 | `pdf.fields`、`pdf.fill`、`pdf.fill_sign` |
Expand Down Expand Up @@ -196,7 +196,7 @@ python3 apps/pdftools/tests/ui.py --shell target/debug/octosense --lock <dir> --

| 运行 | 覆盖内容 |
| --- | --- |
| `shell` | 在 Shell 自己的窗口中、在示例上覆盖每种模式,先浅色,再通过 Shell 自己的样式菜单切到深色(深色这一轮在浅色一轮留下的状态上进行):主页及其封面、状态栏与存储实际字节数相符、“Open a PDF from this device”被拒绝(在没有对话框助手程序的 Linux 上为“Not available here”)、阅读和缩略图、阅读模式(从浮动控制条进入,在其中翻页并改变缩放,再用 Exit read mode 或右上角按钮退出,回到同一页、同一缩放)、只用顶栏的查找(“9 matches on 4 pages”,结束时交还左侧面板)、一处高亮及其回复(日期按本机时钟显示,如“Today 19:42”)、一次旋转及其撤销、带页面范围的合并(“One PDF of 5 pages”)、Fill & Sign 放置的姓名缩写、一次带对齐方式和颜色的文字编辑、保存,以及损坏的文件;之后检查封面缓存中每个 PDF 只有一个封面,且都记在 `library.json` 中 |
| `shell` | 在 Shell 自己的窗口中、在示例上覆盖每种模式,先浅色,再通过 Shell 自己的样式菜单切到深色(深色这一轮在浅色一轮留下的状态上进行):主页及其封面、状态栏与存储实际字节数相符、“Open a PDF from this device”被拒绝(在没有对话框助手程序的 Linux 上为“Not available here”)、阅读和缩略图、阅读模式(从浮动控制条进入,期间窗口处于全屏,在其中翻页并改变缩放,再用 Exit read mode 或右上角按钮退出,回到同一页、同一缩放,窗口恢复原位;之后再用 Esc 退出一次)、只用顶栏的查找(“9 matches on 4 pages”,结束时交还左侧面板)、一处高亮及其回复(日期按本机时钟显示,如“Today 19:42”)、一次旋转及其撤销、带页面范围的合并(“One PDF of 5 pages”)、Fill & Sign 放置的姓名缩写、一次带对齐方式和颜色的文字编辑、保存,以及损坏的文件;之后检查封面缓存中每个 PDF 只有一个封面,且都记在 `library.json` 中 |
| `restart` | 再次使用同一个主目录:资料库、其中的标题、PDF 离开时所在的页面、按导入方式放入的 `Imported PDF.pdf`、所有当前封面都被保留且没有重新绘制,以及从主页移除的 PDF 连同其保留的封面一起删除 |
| `full` | 存储被填到距 64 MiB 只差 8 KB:主页上的拒绝提示,然后移除一个 PDF(会再确认一次) |
| `empty` | 没有 PDF:空资料库及其 Open 按钮,浅色和深色 |
Expand Down Expand Up @@ -257,12 +257,19 @@ python3 apps/pdftools/dev-fixture/make_fixture.py <card-host app data>/os.pdftoo
pdf 服务的测试;在桌面 Shell 中使用真实引擎、按运行时 64 ms 的预算,在 `--edit-sample`
页面上进行的浅色和深色运行:五块的说明、字体和空间的警告以及 Apply anyway、对中文文字的
直白拒绝、一次仍然进行的编辑和一次立即生效的编辑。本次改动后 macOS 上的 Edit **未验证**。
- **已于 2026 年 10 月 13 日验证阅读模式全屏**(Linux 构建主机:Ubuntu 26.04,x86_64,私有的无头
weston;[tests/evidence/read-fullscreen-20261013](tests/evidence/read-fullscreen-20261013/README.zh-CN.md)):
模型测试;基于 #520 的 `18c11642`,使用真实引擎、按运行时 64 ms 的预算进行的 `shell` 运行,浅色和
深色。浮动控制条上的全屏按钮让窗口铺满 Shell 的整个窗口,Exit read mode 和右上角按钮让窗口恢复
原位,页码和缩放保持不变,Esc 同时结束全屏和阅读模式。以下**未验证**:在 macOS 上的同样运行;
OctoSense 窗口进入宿主全屏(隐藏窗口的 Shell 从不请求它);card-host(它不提供窗口服务,阅读模式
在那里应像以前一样占满它的视图,不显示任何提示)。
- **未验证**:宿主自己的文件对话框(Mac 上的隐藏窗口无法打开它;在 Linux 上由替身助手程序
作答);使用真实引擎时的表单字段、大纲、Redo、其他评论和页面操作、受密码保护的 PDF、八个
PDF 的上限以及未保存更改的询问(开发夹具覆盖了它们的界面);其他屏幕尺寸;Linux 上的
X11、GPU 和桌面合成器;Windows;手机。
- **已知问题**:阅读模式占满的是应用窗口,而不是整个屏幕:应用无法让 OctoSense 窗口本身全屏
(没有相应的宿主 API),想要完整效果请将窗口最大化。在负载很高的机器上,一次点按可能超出
- **已知问题**:阅读模式的全屏会请 Shell 让 OctoSense 窗口本身也进入全屏;隐藏窗口的测试 Shell
从不让宿主窗口进入全屏,所以这一步在真实显示器上**尚未验证**。在负载很高的机器上,一次点按可能超出
64 ms 预算而丢失(在 `library.json`
移出点按路径之前,于 60 到 150 的平均负载下见到过)。pdf 服务的文件写入在 UI 线程上运行,
也计入这个预算(在 54 到 74 的平均负载下,曾在 262 ms 和 555 ms 的重命名之后超出预算)。在 900 点高的屏幕上(应用区域约为
Expand Down
27 changes: 24 additions & 3 deletions apps/pdftools/bundle/main.splash
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,7 @@ if st == nil {
busy_at: 0 // when the background render went out
heartbeat: nil // the interval below, made once
index_dirty: false // library.json waits for the heartbeat
screen_full: false // the shell has the window in full screen for Read mode (on_app_fullscreen)
}
mod.pdftools = st
}
Expand Down Expand Up @@ -1362,15 +1363,34 @@ fn refit(t){
fn reading_on(t){ t != nil && optional(t, "reading", false) && t.state == "ready" }
// From the floating bar's full-screen button: the same page, and the zoom
// the person chose, or the fit, worked out for the whole window (kept_zoom).
// The tap also asks the shell to put the window in full screen
// (window.fullscreen). Where it can't (no tap it counts, a host with no
// window service, such as card-host), Read mode fills the window as before,
// and the answer is not shown.
fn enter_reading(t){
if t == nil || t.state != "ready" || t.mode == "pages" || t.mode == "combine" { return }
t.reading = true
reading_zoom(t)
show()
go_page(t, t.page)
}
// From the bar's Exit read mode or the corner control (a script gets no key
// events, so there is no Esc): the page and zoom it left Read mode with.
host.request("window.fullscreen", {on: true}, fn(r){})
}
// The shell's word that the window's full screen started or ended: when
// Esc, ⌘F or another window ends it, Read mode ends too. One that starts
// after Read mode already ended goes straight back.
fn on_app_fullscreen(on){
st.screen_full = on
if on { screen_back() } else { leave_reading(cur_tab()) }
}
// The full screen Read mode asked for goes back once Read mode is over,
// however it ended (show() asks after every change).
fn screen_back(){
if !optional(st, "screen_full", false) || reading_on(cur_tab()) { return }
st.screen_full = false
host.request("window.fullscreen", {on: false}, fn(r){})
}
// From the bar's Exit read mode or the corner control, or the shell's Esc
// (on_app_fullscreen): the page and zoom it left Read mode with.
fn leave_reading(t){
if t == nil || !optional(t, "reading", false) { return }
t.reading = false
Expand Down Expand Up @@ -2506,6 +2526,7 @@ fn show(){
let t = cur_tab()
let doc = t != nil
let c = chrome_for(reading_on(t))
screen_back()
ui.home.set_visible(!doc)
ui.doc.set_visible(doc)
ui.tabs_box.set_visible(c.tabs)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": [0, 0, 1400, 900], "window": [1400, 900]}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": [54, 76, 1292, 662], "window": [1400, 900]}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": [54, 76, 1292, 662], "window": [1400, 900]}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading