Repository navigation
PDF Tools v2: rebuilt from the approved designs on the pdf service v2 - #473
Conversation
535e792 to
1ca87f6
Compare
v1 (#430) collapsed pdfcraft's desktop interface into a phone-style flow over five of the engine's 132 commands. v2 starts over through OctoSense App Flow: product requirements, UX design by image generation, pixel mapping to widgets, then the logic. - design/BRIEF.md: the product requirements (jobs, desktop layout, screens, states, every action mapped to its engine command, constraints). - design/source/: the exact prompt for each of nine screens and the untouched gpt-image-2 outputs at 1536 x 1024, with generation.json provenance. The maintainer approved generation and the designs. - design/measure_desktop.py and source/*.measured.json: App Flow's observe/measure assume a 406 x 776 phone artboard, so this applies the same measurement at the desktop screen's own size, reusing App Flow's ocr.swift: text runs with ink and ground colours, surfaces with fill, border and corner radius, and one-pixel dividers. - design/SERVICE.md: the app-facing pdf service contract the app and the service build against (open documents, page renders, find, comments, Fill & Sign, pages, text edits, undo, redo, save). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pdf service v2 (#472) settled what the draft left open: - the render cache holds at most 16 MiB and 64 files per app, since app storage allows 256 entries and imports and library saves need room; - pdf.export images: at most 64 pages and 256 megapixels a call, each page at most 16 MP; export text takes pages too; - doc is a lowercase hex string; every rectangle is [x, y, w, h] in points, and find's match rects can go straight into a highlight; - pdf.open's fields and comments are counts; - a save to a new path re-points the open document; a plain save from a background surface answers invalid:; - comments: local dates without zone or seconds, one bounding box, ids of unnamed comments @page-index, Fill & Sign marks in the list, and the person's name always passed (the engine otherwise writes "PdfCraft"); - find's total saturates at 10,000; a fractional dpi is rounded and an out of range one is invalid:, and a render over 16 MiB is too_large:; - invalid: also covers paths outside storage, taken output names, nothing to undo or redo and edits to read-only documents; - merge ranges accept "all", "" and typed dashes; rotate accepts -180; field options are {value, label}, and fields gain read_only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
App Flow's next step after the approved designs: design/map/NN-<name>.json ties every measured text run, surface and divider of each screen to one region, with its role, the Splash widget that rebuilds it and the runtime source it was read in, its geometry in points (fixed rail and panels, a flexible canvas), its light colours and its dark ones from 09-dark-reading, its type size from the run's ink (calibrated on the runtime's own text in card-host) and the review decision that applies: one close cross on the report tab, no Document panel in Find, no pill in Combine, "Alignment" for "Aligment", and every page the engine's render. Nothing is left unmapped; page contents are marked as sample art. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The window the designs share (document tabs, the mode bar with search, Undo, Redo and Save, the rail and its panels, the canvas and its pill, the status line) and Home, Read, Find, Comment, Fill & Sign, Pages, Combine and Edit, wired to SERVICE.md's methods and nothing else: one document per tab, renders at screen resolution one at a time and kept, progressive thumbnails, find and comment rectangles as overlays, fields, page operations, paragraph edits, undo, redo and save, combine with page ranges, and Open a PDF from this device titled with the file's name. The states the brief lists: an empty library, a page rendering, damaged, protected, storage full (Remove a PDF, which asks once more), eight PDFs open, unsaved changes when closing, no engine. Light and dark from the shared interface's appearance with the maps' colours; state on mod.pdftools across a restyle (#440). Outline icons drawn to the designs' glyphs. Version 0.2.0. The runtime gives script apps no scroll position and no key events: going to a page draws the canvas empty for a frame, which clamps its scroll to the top, then fills it with a run from that page; the shortcuts are buttons. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tests/ui.py walks every mode with the real engine in a hidden desktop shell (shell, restart, full, empty), the shipped bundle in card-host without host services (missing), and the dev fixture in card-host (fixture): every designed screen at the designs' 1536 x 1024, light and dark, each grab beside its design, and the damaged, protected and unsaved states. The fixture stays outside the bundle: a stand-in engine for SERVICE.md's methods, spliced into a scratch copy, and sample documents drawn from the designs' own sample text. v1's shell evidence goes: it showed a different app. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Page ranges as Combine takes them, find snippets with the match in bold, avatar initials and the service's comment dates, beside the library's title rules in pdftools_model_tests.rs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1ca87f6 to
755f166
Compare
The shell opens PDF Tools at 990 x 603 points (72% x 76% of the desk, at most 1000 x 720), not the designs' 1536 x 1024. Below 1180 points the mode tabs and Save trim their padding, both right panels take 320 points, and until the person zooms the page fits the canvas (up to 100%) as modes, panels, find results and the window change. Tab titles share the row so Open always shows; the Pages tool row drops its hint when narrow; the in-place editor grows to hold its text; borderless text fields draw no bevel; the status line counts kilobytes under 1 MB. library.json is written by the heartbeat instead of in taps, so a tap never waits on the disk within its 64 ms. tests/ui.py: the shell journeys as the real engine answers them (the app's own pictures only, clicks at the pill's zoom, the pill's next page, Combine's range fields and button, a typed edit, a dark pass that works on the light pass's state), and the fixture journey opens the designs' five PDFs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tests/evidence/v2-20261010: the hidden desktop shell with the real pdf engine (shell light then dark, restart, full, empty) at the runtime's 64 ms script budget, card-host without services (missing) and with the dev fixture beside the nine designs (light and dark, plus the damaged, protected and unsaved states), shell frames beside their designs, and a receipt with digests, commands, checks and what was not verified. READMEs in both languages: the desktop's window size and the narrow layout, the journeys as they now run, the budget option for a busy machine, and the status: what was verified, what was not, and what is known (comment dates in UTC from the service, storage use counted from the library). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Local CI passed on a5878ee ( GitHub would run for this PR: desktop.yml, phone.yml, apps.yml. Merged on this local pass (macOS runner queue); GitHub CI runs on the merge commit on main.
Skipped (these did NOT pass; (!) marks a skip that blocks tools/ci-local-merge.sh):
Notes:
|
`gh pr diff --name-only` refuses a diff over 20,000 lines, so the merge script could not work out which workflows OctoSense-org#473 triggers (it adds about 34,000 lines of generated designs and evidence frames) and refused it. List the files from GitHub's paginated pulls/<n>/files instead, with a renamed file's old path as the pull_request path filters see it, and refuse when GitHub lists fewer files than the PR changes (the listing stops at 3,000). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PDF Tools v2, rebuilt through OctoSense App Flow from the nine designs approved on 10 Oct 2026, on the pdf service v2 (#472, merged). Draft: do not merge.
What's here
apps/pdftools/design/map/NN-<name>.json: the pixel maps (App Flow step 1). Every measured text run, surface and divider of each screen is tied to one region: its role, the Splash widget that rebuilds it with the runtime source it was read in (cite), geometry in points (fixed tabs, rail and panels, a flexible canvas), light colours and dark ones from09-dark-reading, type size from the run's ink (calibrated on the runtime's own text in card-host), and the review decision that applies.coverage.unmappedis 0 on every screen; page contents are marked as sample art.apps/pdftools/bundle/main.splashv2 (0.2.0, App Flow step 2): the shared window (tabs, mode bar with search, Undo, Redo, Save, rail and panels, canvas and pill, status line), Home, Read, Find, Comment, Fill & Sign, Pages, Combine and Edit, wired todesign/SERVICE.mdonly (step 3).bundle/icons/: 46 outline icons drawn to the designs' glyphs.apps/pdftools/design/SERVICE.md: the contract as built in pdf service v2: open documents for PDF Tools v2 #472, in its own commit, as authorized.apps/pdftools/dev-fixture/andapps/pdftools/tests/ui.py: a stand-in engine and the designs' sample documents for card-host runs, outside the bundle (Quick Deck's pattern), and the end-to-end journeys.crates/shell/src/pdftools_model_tests.rs: four more pure rules tested from Rust (10 in all).apps/pdftools/tests/evidence/v2-20261010/: the runs' frames, side-by-sides and receipt.Review decisions applied: one close cross on the report tab; Find has no right panel; Combine has no pill; "Alignment"; every page is the engine's render (
pdf.page), with highlights, matches and fields as overlays from the engine's rectangles; no dev or replay path in the bundle.The maps, per screen
pdf.comments.pdf.edit_texttakes only text); the font box is read-only.pdf.open).Verification
All on macOS (Apple silicon), 10 Oct 2026; frames, side-by-sides and
receipt.json(digests, commands, checks) inapps/pdftools/tests/evidence/v2-20261010/.cargo test --locked -p octosense-shell --lib pdftools_model: 10 passed.hub stamp,hub check --system-app): only the listing refusal every system app gets, and the unsigned warning.tests/ui.py --shell, one instance under the shell lock, Terminal.app tab,MAKEPAD_HIDE_WINDOWS=1, ended with/quit):shell(light, then dark through the shell's own style menu),restart,full,empty: passed, no error or budget lines in any log. Home with the engine's covers; "Open a PDF from this device" refused (foreground_required: a hidden window is never in front); reading and thumbnails; find ("9 matches on 4 pages", the match highlighted on its page); a highlight frompdf.lines' box and its reply; page 2 turned and Undo; Combine with a range ("One PDF of 5 pages") and the new PDF opened; initials placed on the lease; a paragraph edited in place (pdf.edit_text, the page re-rendered with it); Save (a plain save works from the hidden shell); the damaged file; after a restart the import placed in the library opens and the report returns to its page; with storage full, the notice, then a removal that asks first; the empty library, light and dark.tests/ui.py --card-host):missing(no engine, no files service) andfixture(the nine designed screens at 1536 x 1024 beside their designs, light and dark, plus the damaged, protected and unsaved states): passed.missingran at the runtime's 64 ms script budget (load average 10 to 30); the shell runs passed again with--budget-ms 1000while other sessions' builds held the load between 45 and 117.fixtureran with--budget-ms 1000: its stand-in engine answers in script (it parses the fixture's JSON), and at 64 ms its journey passed but one stand-in answer overran in the dark pass. Two earlier rounds at the default budget, beforelibrary.jsonmoved off the tap path, lost the tap that opens a PDF to an overrun at load 60 to 150 (see Runtime gaps).library.jsonwritten off the tap path; the harness now clicks only the app's own pages, at the pill's zoom.What still differs from the designs, and why
Side-by-sides:
apps/pdftools/tests/evidence/v2-20261010/fixture/(card-host at the designs' 1536 x 1024, design left, app right, light and dark) andcompare/(the real-engine shell frames beside their designs at the shell's height).Runtime gaps (with sources)
set_visible,is_visible,renderandrender_styleonly (makepadwidgets/src/view.rs:876-910), and a scroll is clamped to its content on every draw (widgets/src/scroll_bar.rs:1264-1267). Going to a page draws the canvas empty for one frame, then a run of pages from that page.on_changeandon_returnonly (widgets/src/text_input.rs:675-705), and the shell's window manager takes the Cmd chords. The brief's shortcuts are buttons.widgets/src/label.rs:321-342: the padded walk goes to its turtle and to its text); a view whoseon_renderstarts hidden stays hidden (observed): wrappers are shown and hidden instead.widgets/src/widget_async.rs:505-515,splash_entry_budget,MAKEPAD_SPLASH_BUDGET_MS): at load averages of 60-150 a tap that opens a PDF and redraws the window can overrun, and the overrun drops the action. Seen twice in runs at the default budget, both on the tap that opens a PDF (once in the shell, once in card-host). The app now writeslibrary.jsonfrom its heartbeat instead of in taps.on_app_resize(only the shell does,crates/shell/src/module_view.rs:603-618), so card-host runs use the app's default width.border_sizeis 0 (widgets/src/text_input.rs:101-191).Window size
crates/shell/src/desktop_layout.rs:19-34,DesktopWindows::ensure). PDF Tools got 990 x 603 points in a 1400 x 898 shell window.AppManifesthas no size field and refuses unknown keys (app-contract/src/manifest.rs:290-345at the pin,deny_unknown_fields);system-apps.jsonlists ids only; and the shell ignores the size of a module'sWindowRequest::Open(crates/shell/src/lib.rs:2823,size: _).on_app_resizefollows every size and style change (module_view.rs:603-618). I did not patch the shell. If a larger default is wanted, the smallest change is a manifest hint (a preferred size) thatDesktopWindows::ensurehonours within the desk.Contract changes needed
pdf.commentsdates are UTC, not local. The engine writes/M (D:20261011003256Z)for a mark made at 17:32:56 PDT; its listing drops the zone, andreview.rs::datepasses2026-10-11T00:32on as local time, so PDF Tools shows "11 Oct" on 10 Oct. The service should convert to local time, as SERVICE.md says, or keep the zone (…Z) so the app converts withlocal_time.pdf.edit_textalignandcolorfor 08's Alignment and Colour.files.status(or a storage method) reporting the app storage's used and total bytes, for the status line and the full-storage state.pdf.close. A closed document's renders go with it (doc_tests.rs, "a closed document's renders go"), so Home re-renders every cover after each start (open, info, page, close per PDF on the UI thread). For examplepdf.pagewith anoutpath, orpdf.infowith a small first-page render.pdf.exportis already in the contract.Not verified
pdf.fields,pdf.filland the field overlays ran only on the dev fixture), outlines (the samples have none), Redo, underline, strike-out, notes, text boxes, statuses and deletes, page delete, extract, insert and move, password-protected PDFs, the eight-PDF cap, and the unsaved-changes question. The dev fixture covers their screens.🤖 Generated with Claude Code