Skip to content

QuillPost 1.5.0: rename, command set and QAT, AI tab, embedded QuillPen, every mail format, phone layout - #4

Merged
socrtwo merged 28 commits into
mainfrom
ccr-90803495-l7seer
Oct 17, 2026
Merged

socrtwo merged 28 commits into
mainfrom
ccr-90803495-l7seer

Conversation

@socrtwo

@socrtwo socrtwo commented Oct 17, 2026

Copy link
Copy Markdown
Owner

Merges the current QuillPost work into main.

Summary

  • Rename Quillbox → QuillPost across code, packages, help and metadata; app IDs follow the suite pattern (io.github.socrtwo.quillpost). Existing setups keep working: ~/.quillbox moves to ~/.quillpost on first start, QUILLBOX_* settings and quillbox.* properties are still read.
  • Command registry and Quick Access Toolbar: ~400 commands usable from the ribbon, QAT, context menus, shortcuts and command search; Outlook's QAT choices plus Add All (Save, Print first, then A–Z); Outlook's command set for IMAP/POP3/SMTP, commands from Eudora, Pine/Mutt, Pegasus, Lotus Notes, Thunderbird, Gmail, Apple Mail, Hey/Superhuman/Spark, Claris Emailer and Opera, and 17 new ones (docs/COMMANDS.md).
  • AI tab with provider wizard, on-device model, email commands, and several AIs per request (master or Quaker consensus) (docs/AI.md).
  • Embedded QuillPen editor when QuillPen is installed (Android ContentProvider, desktop install folder on a separate origin, web URL), per docs/EMBED.md.
  • Opens every mail format, detected by content: PST/OST (ANSI, Unicode, 2013), all mbox dialects, Gmail Takeout, Maildir, MH, Apple Mail, Outlook Express DBX/MBX, Eudora, Pegasus, The Bat!, .eml, .msg, .oft, winmail.dat, .emlx, .mht, vCard, iCalendar; Import Mailbox into an IMAP folder or a read-only, searchable Local Archives; office files handed to QuillPen / QuillGrid / QuillStage (docs/FORMATS.md).
  • Layout: 7,092 audit findings fixed (QAT widening the page on phones, toasts under dialogs, bottom QAT covering content, AI pane under system bars, reading pane squeezed by the AI pane, menus off-screen); web/src/test/e2e/layout.mjs covers every command on phone and desktop profiles. Scroll cues, safe areas, suite icon.
  • Version 1.5.0.

Testing

  • JS: node --test web/src/test/js/*.test.js — 88 pass.
  • Kotlin: cd web && ./gradlew test — 175 tests, all pass except NetTest's live MX lookup (no DNS in the build sandbox; passes with network).
  • e2e: openfile 38/38, mailimport 21/21, embed 64/64, layout audit 0 defects.
  • Not run here: Android and iOS builds (no SDKs), desktop installers (CI builds them).

Generated by Claude Code

claude added 28 commits October 17, 2026 05:54
Implements QuillPost's side of the suite embed protocol:
- compose: a QuillPen button (and openInQuillPen() for the command
  registry) opens the body in an iframe that fills the compose body;
  postMessage protocol with source and origin checks, 8 s ready
  timeout with fallback to the built-in editor, Send and draft saves
  request the current content first, Cancel keeps the old body,
  incoming HTML is sanitised before it enters the page
- Settings > Compose: Edit messages in QuillPen (Off/Ask/Always) when
  QuillPen is found; QuillPen folder (desktop) or web address (web);
  hidden on iOS
- Android: <queries> for the QuillPen package and provider, detection,
  https://quillpen.embed/<path> answered from QuillPen's ContentProvider
  with MIME types, availability exposed to the page
- desktop: finds resources/embed in the standard install locations or a
  chosen folder (native picker) and serves it at /quillpen-embed/<path>
  with path-traversal checks
- tests: unit tests for path resolution and install locations; a fake
  QuillPen page and a Playwright test of desktop, web, Android and iOS

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
Installs the suite scroll cues (thin scrollbar that widens when used,
edge shadows, a More chip) on every menu, autocomplete list, dialog,
settings page, folder and side pane, provider grid, rule preview and
My Day; long content lists (messages, contacts, tasks, reading pane,
editors, calendar grid) get the thin scrollbar without the chip. Bars
that scroll sideways on narrow screens (ribbon, compose tabs and
formatting bars, settings tabs) get edge shadows. Colours follow the
app's light and dark tokens.

Fixes in the shared module (applied to the suite copy too): the chip
of a container inside a modal dialog now lives in that dialog, since
anything outside the top layer is drawn under it and inert; only
containers that can really scroll get a chip; the chip is placed in
the part of the container that is not clipped by a scrolled ancestor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
- Android: adaptive-icon foreground converted path for path from the
  suite's icon-foreground.svg, background #0D7C86, splash icon is the
  full gradient tile inside the splash mask
- iOS: 1024 px app icon (full bleed, no alpha) and launch logo
- desktop: .png/.ico/.icns installer icons wired into the Compose
  native distributions; window and tray icon
- web: SVG favicon, Apple touch icon, the start-up splash mark
- branding/QuillPost holds the suite sources; scripts/gen-icons.mjs
  regenerates every raster from them

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
…ands

The Mail ribbon gets an AI tab (also reachable from the reading pane's AI
button, the compose window's AI tab and an AI assistant pane). Ported from
the sister apps' AI modules to plain JavaScript under web/ai/:

- provider registry: on-device Transformers.js model in a Web Worker, the
  browser's built-in model, free tiers, Ollama / LM Studio, paid keys and
  any OpenAI-compatible service; wizard with grouped providers, key test
  and live model list; Active AI, Providers & models, AI settings, usage,
  AI & Privacy, Stop, Delete downloaded models, Prompt library
- client: OpenAI-compatible, Anthropic and Gemini request builders, SSE
  streaming with abort, parameter retry, usage; providers without CORS go
  through a new backend relay (/api/ai/proxy) that only forwards to the
  provider table's hosts and addresses the account registered
- email commands: summarize message/thread, draft reply (tone, length,
  accept/decline/details/thank), rewrite, translate, proofread, subject
  line, template, action items -> To Do, events -> Calendar, contacts ->
  People, classify, explain jargon, phishing check, follow-up, ask about
  this mailbox (headers only by default), unsubscribe helper
- changes are previewed and applied as one undoable step; every answer
  shows what was sent
- keys: Android Keystore-encrypted store via a new QuillboxAndroid bridge,
  the desktop backend's private data folder (owner-only permissions), or
  IndexedDB on the web; never in settings or exports

Tests: node --test web/src/test/js (wired into `check` as jsTest) and
AiRoutesTest for the relay allow-list and desktop key file. Docs in
docs/AI.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
- Insert/Replace in a draft now selects inside the user's own text, checks
  that the quoted original and the signature are unchanged afterwards, and
  places the text directly if the browser merged it into them (Chrome did,
  dropping the quote's rule)
- The relay never connects to link-local addresses, and reaches loopback or
  private addresses only for browsers on loopback or a private network
  (QUILLBOX_AI_RELAY_PRIVATE=on/off overrides), so an internet-facing
  Quillbox cannot be used to reach its own network

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
Every action is now a registered command shared by the ribbon, a Quick
Access Toolbar, context menus, command search (Alt+Q) and customisable
keyboard shortcuts (with Gmail- and Mutt/Pine-style schemes).

- commands.js: registry, key names and keymaps, QAT model (standard
  Mail choices, Add All Choices with Save and Print first, sanitising)
- qat.js / commands.css: QAT above/below the ribbon or at the bottom,
  drop-down checklist, More Commands chooser, import/export, touch mode,
  command search, keyboard customisation, command reference, File
  backstage, Send / Receive, Folder and Tools ribbon tabs, compose
  Review and Draw tabs
- mailcommands.js: the desktop client's Mail commands (File, Home,
  Send / Receive, Folder, View, Help, Message, Insert, Options, Format
  Text, Review, Draw), Undo/Redo of mail actions
- mailextra.js / mailtools.js: commands from Eudora, Pine/mutt,
  Pegasus, Lotus Notes, Thunderbird, Outlook Express, Gmail, Apple Mail,
  Hey/Superhuman/Spark, Claris Emailer and Opera M2, plus new commands
  (time slots, thread digest, waiting for reply, one-click unsubscribe,
  header inspector, quote integrity, triage…)
- Backend /api/x/* endpoints: redirect/bounce with Resent-* headers,
  IMAP keywords and flags, compact, mark read by date, archive by
  year/month, folder stats and copy, large messages, strip attachments,
  subject edit, mailing lists, RFC 8058 unsubscribe, header inspector,
  awaiting replies, outbox flush, muted/routed conversations, state store
- Message summaries carry IMAP keywords
- Tests: Node unit tests (web/src/test/js) and CommandFeaturesTest.kt
- docs/COMMANDS.md and a README section

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
IMAP deletions already expunge explicitly, so closing a folder no longer
expunges as well: a Pine/mutt "mark for deletion" now survives until
Compact Folder. POP3 still deletes on close. Messages rewritten by
Delete Attachments or Edit Message no longer raise a new-mail notice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
The suite regenerated icon-foreground.svg so that the quill tip stays
inside Android's 66dp safe circle (it was clipped in round launcher
icons); the vector drawable follows it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
Takes the suite's newer scrollcue.js/.css (no hover widening, chips
stay inside narrow containers, no stacked chips for nested scrollers,
keyboard-aware placement, destroy()); it already contains the fixes made
here (chip inside the modal dialog's top layer, real scrollers only).
Dark mode sets the chip text colour through --sc-on-accent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
A phone layout audit (web/src/test/e2e/layout.mjs) opens every dialog,
menu, drawer and overlay at 412x915, 360x640 and 915x412 with system-bar
insets and the keyboard, and checks the safe area, covered or
overlapping controls and sideways scrolling. Defects it found, fixed:

- collapsing the folder pane (wider than 700 px: landscape phones,
  tablets, desktop) left the message list in a zero-width grid column,
  so the mail view went blank
- the immersive reader's toolbar did not wrap on phones: the dialog
  scrolled sideways and its Close button was off screen
- toasts covered the New mail button on phones and the Send button of
  a message docked in the reading pane
- with the keyboard up on short screens (landscape, 360x640) the
  message window squeezed the editor to nothing and clipped Send; it
  now fills the screen and scrolls, keeping the editor usable
- in a browser or home-screen web app (viewport-fit=cover) nothing
  respected env(safe-area-inset-*): the title bar, bottom navigation,
  full-screen dialogs, menus, drawers, toasts and the New mail button
  sat under the status bar, home indicator or cut-outs

safearea.css/js add --sa-* inset variables (env() by default), pad the
page and place dialogs, drawers, toasts and floating buttons inside the
safe area, keep pop-up menus inside it, and measure keyboard overlap
from visualViewport where the browser does not resize the page; the
viewport asks for interactive-widget=resizes-content. The Android and
iOS shells already pad the WebView natively (insets stay 0 there).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
AI settings gain "Use several AIs": tick two to six configured providers
(each with a model), pick a master/clerk, the rounds of discussion and
whether to show the discussion. In master mode every AI answers and the
master writes the best answer from the shuffled, anonymized responses. In
consensus mode a clerk drafts a minute, the AIs unite, stand aside or
block, and blocks send the minute back for revision; without unity the
clerk's last minute is returned and the objections are shown.

The orchestration lives in ai/ensemble.js and depends only on an injected
call(), so it is tested with mock providers. The client's chat() uses it
for requests that name no provider; each member call keeps its own route,
including the relay fallback. Progress reaches the AI pane and previews,
"How the AIs answered" shows each answer, round and outcome, and commands
apply only the final answer. The AI tab gets Several AIs, Choose AIs and
Show AI Discussion (also in QB_AI_COMMANDS); the cost note and every
privacy notice list each service that receives the text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
Serve the desktop embed on a separate origin (localhost instead of
127.0.0.1) so the embedded editor cannot read QuillPost's storage, and
register Edit in QuillPen as a command for the Quick Access Toolbar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
Register the AI tab's commands in the command registry so they can go on
the Quick Access Toolbar, custom tabs, shortcuts and command search.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
Every name, identifier, package, path, help page and metadata entry now
says QuillPost. App IDs follow the suite pattern io.github.socrtwo.*.
Existing installs keep working: ~/.quillbox is moved to ~/.quillpost on
first start, and QUILLBOX_* settings and quillbox.* properties are still
read when the new names are not set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
… office files

Adds the fileformats package: a compound-file reader (MS-CFB), Outlook .msg/.oft
items (MS-OXMSG: messages, contacts, appointments, tasks, embedded messages,
named properties), winmail.dat (MS-OXTNEF), compressed RTF (MS-OXRTFCP), HTML
de-encapsulation (MS-OXRTFEX) and an RTF-to-HTML renderer, a forgiving MIME
reader (RFC 2047/2231, 8-bit headers, UTF-7, broken boundaries, format=flowed,
text/enriched, uuencode, yEnc, BinHex, AppleSingle/AppleDouble, S/MIME and PGP),
MHTML, .emlx and .nws, vCard 2.1 quirks, Windows .contact, vCalendar 1.0, and
content detection of word-processor, spreadsheet and presentation files.

MessageFormats.detect/parse/toRfc822 is the API for the import pipeline. Tests
build every fixture from the specifications.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
Content-based detection of mailboxes, single messages and office documents,
readers for mbox (all dialects, Gmail Takeout), Maildir, MH, Apple Mail,
Eudora, Outlook Express (.dbx, OE4), Outlook .pst/.ost/.pab (clean-room
from MS-PST, with compressed RTF and encapsulated HTML), Pegasus Mail and
The Bat!, an import pipeline into IMAP folders or a new read-only Local
Archives store, and the File > Open / Import Mailbox UI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
New "Open file" feature: mail.openFile (File > Open & Export, or drop a file
on the window) sends the file to /api/file/open, which recognises it by content
and returns a message, contacts, events or a document description. Messages
open in a reader window with Reply, Reply all, Forward, Save to folder, Save as
.eml, Headers and their attachments (attached messages and documents open in
turn); images load only from the file itself, remote images and tracking
pixels stay blocked. Outlook templates open as a new draft. vCards and Windows
contacts go to People, iCalendar and vCalendar files to the calendar.
Documents, spreadsheets and presentations offer Open in QuillPen, QuillGrid or
QuillStage and Send as attachment (mail.sendFileAsAttachment).

sanitizeHtml takes an optional cidUrl so inline parts of opened files resolve.
Sample files, JS unit tests and a Playwright check (src/test/e2e/openfile.mjs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
…rchives, browser test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
…s, Open with, share)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
…fixes

Android: "Open with QuillPost" (messages, contacts, calendars and generic
binary files) and "Share to QuillPost" hand the files to the in-process backend,
which the page picks up and shows; "Open in QuillPen/QuillGrid/QuillStage"
starts the suite app with a content:// URI and falls back to any viewer.

Desktop: file arguments and macOS open-file events are opened in the UI, a
second launch hands its files to the running instance (key-protected local
route), documents open with the program the system registered for them, and
the installers register .eml, .emlx, .nws, .msg, .oft, .mht, .tnef, .vcf, .ics
and .vcs.

Readers: recipients and attachments of Outlook items use the message's code
page; plain text is derived from HTML-only bodies; subtype-less content types,
wrong boundary parameters, delivery reports, inline text parts and
Content-Description names are handled. Opt-in ExternalSamplesTest runs the
readers over a folder of real files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
… Outlook items

docs/FORMATS.md gets "Messages, contacts and calendars" (program, versions,
extensions, detection signature, fidelity tier and limitations for every
single-item format) and "Documents, spreadsheets and presentations" (what is
recognised and handed to QuillPen, QuillGrid or QuillStage). The README
describes opening files. Fixture sources and licences are recorded in
web/src/test/resources/fixtures/README.md.

Outlook S/MIME items (IPM.Note.SMIME*) now show the message inside their
smime.p7m attachment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
… and detection

Single messages, contacts, calendars and office files are detected and read by the
fileformats package; PST/OST items go through its MAPI converter (original transport
headers now kept by MimeWriter); mailboxes opened from Open File, drops or the shells
are routed to Import Mailbox. The duplicate Open File command, RTF code and shell
hand-off are removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
…ones and desktops

The phone layout audit failed again after the command registry, Quick Access
Toolbar, AI tab, file-open window and mailbox import were merged. Root causes
and fixes:

- Title bar: with the Quick Access Toolbar in it, its buttons were wider than a
  360 px screen, so the browser widened the layout viewport and every page,
  dialog, menu and the New mail button ran off the screen. The search box now
  gives way (with a minimum), the toolbar shrinks and scrolls, and narrow phones
  keep one toolbar command.
- Toasts were drawn beneath an open modal dialog (Undo could not be pressed):
  the toast host now moves into the top modal dialog as a line at its foot.
  Modal dialogs are tracked in opening order, so menus, toasts and the AI busy
  chip go to the dialog that is really on top.
- The Quick Access Toolbar at the bottom floated over the list, the New mail
  button and a docked reply's Send button: it is now a bar of its own under the
  app, and floating buttons, toasts, drawers and panes keep above it (--app-b).
- AI pane: below the title bar and clear of the system bars, the keyboard and a
  bottom toolbar; full height and scrolling in short windows. On wide screens
  the mail panes share what is left so the reading pane keeps a readable width,
  and its toolbar wraps when narrow (container query) instead of setting the
  subject one letter per line. The AI busy chip is a line of its own.
- Menus anchored to a control scrolled out of view are kept inside the safe
  area; the app rail scrolls in short windows; very short windows scroll the
  module instead of squeezing the list; settings rows wrap their buttons.
- Help and Report a problem no longer navigate the app away (window.open with
  "noopener" always returns null).

The layout audit now also opens every command of the registry, the Quick
Access Toolbar in all positions with and without labels, its drop-down and
chooser, the keyboard dialog, command search and reference, the File
backstage, every ribbon and compose tab, the AI pane, setup, settings with
several AIs and "How the AIs answered", Import Mailbox start/options/progress/
report, Local Archives, the open-file window and the embedded QuillPen editor,
on 412x915, 360x640, 915x412 (insets and keyboard) and 1280x860 / 1500x900.
It detects pages wider than the screen, controls cut off where they cannot be
scrolled to and text squeezed into a sliver. New scroll containers get scroll
cues.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQ7NPUMmuJkkxN28Whzi6Q
@socrtwo
socrtwo merged commit dcc4652 into main Oct 17, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants