mirror of
https://github.com/priyanshujain/margin.git
synced 2026-10-03 19:47:03 +00:00
105 lines
6.4 KiB
Markdown
105 lines
6.4 KiB
Markdown
# App facts
|
|
|
|
Things true of exactly one app. Everything in the other files applies to all four.
|
|
|
|
## Margin, the writing studio
|
|
|
|
`python/margin`, package `margin-app`, bundle id `studio.margin.app`, version 0.1.17, FSL-1.1-MIT.
|
|
The oldest and most polished of the four, and the one that ships first on any new channel.
|
|
|
|
Library data is self-contained `{book-id}.margin` JSON files with images embedded as base64, plus
|
|
`custom-dictionary.txt`, in `~/Library/Application Support/studio.margin.app/library/`. That
|
|
simplicity is what made the Drive backup feature small.
|
|
|
|
Google Drive backup is built and lives in `src-tauri/src/gdrive.rs`, with `src/backup.ts`,
|
|
`src/store/useBackup.ts`, `BackupButton` and the `BackupSettings` panel on the front. Loopback plus
|
|
PKCE through the system browser, scope `drive.file` and `openid email`. `drive.file` is
|
|
non-sensitive, so there is no Google verification review and no CASA audit, and the consent screen is
|
|
set to Production to avoid the unverified warning and the seven-day refresh token expiry that testing
|
|
mode imposes.
|
|
|
|
The Drive layout is a visible `margin/` folder, one file per book, latest only, because Drive keeps
|
|
revisions itself. Backup triggers are manual, on app close, and every 15 minutes, all gated on a
|
|
dirty check so nothing uploads unless something changed. The cloud icon opens the panel rather than
|
|
backing up in one click, because the panel is the single home for back up, restore, disconnect and
|
|
account. Restore is offered as an ignorable link on the empty-library state, which doubles as
|
|
connect-on-a-new-machine.
|
|
|
|
The refresh token and sync state sit in plaintext in `backup.json` in the app data directory. That
|
|
was a deliberate step back from the keychain, for the reasons in [code-style.md](code-style.md), and
|
|
it matches the app's existing local-data model given the limited scope. The newer apps seal theirs
|
|
with XChaCha20-Poly1305 instead, and Margin should be brought up to that.
|
|
|
|
Margin has no test script at all. It is the only one of the four without one.
|
|
|
|
`appstore/` and `target-mas/` are the Mac App Store build track.
|
|
|
|
## Margin Calendar
|
|
|
|
`python/margin-caledar`, package `margin-calendar`, bundle id `studio.margin.calendar`, MIT. The
|
|
directory name really is misspelt.
|
|
|
|
Its `docs/design.md`, `docs/conventions.md` and `docs/architecture.md` are the model the rest of the
|
|
suite documents by. When writing docs for another app, follow those.
|
|
|
|
Prev and next move one day, never a week. Week view is a rolling seven days from the anchor by
|
|
default, with `weekMode()` in `src/time.ts` stored under `margincal-week-mode` and `weekAnchor()` as
|
|
the single source both `spanFor` and `GridView` use. Never reintroduce a bare `startOfWeek` call in
|
|
either. The snapping "calendar" mode exists in Settings, and only there do the arrows step by a week,
|
|
because a day step would be invisible.
|
|
|
|
The vertical axis has contraction hysteresis for the same reason the horizontal one slides by a day:
|
|
positional memory is most of the speed of a keyboard-driven calendar.
|
|
|
|
Persisted localStorage keys are `margincal-folds`, `margincal-bounds`, `margincal-view` and
|
|
`margincal-theme`. See [platform.md](platform.md) for how to read them out of the installed app.
|
|
|
|
This is the app with the Nix flake, and the one whose Linux story the others should copy.
|
|
|
|
## Margin Docs
|
|
|
|
`rust/margin-editor`, package `margin-docs`, remote `margin-docs`, MIT. Three names for one thing,
|
|
and the directory is the odd one out.
|
|
|
|
No memory files and no `CLAUDE.md` exist for this app, so nothing was ever written down about it. It
|
|
is also the largest front end in the suite at 43,000 lines, and it is a near twin of Margin on the
|
|
Rust side: `pdf.rs`, `fonts.rs`, `macspell.rs` and `writingtools.rs` exist in both. That duplication
|
|
is the subject of `../typesetting.md`.
|
|
|
|
## Margin Mail
|
|
|
|
`rust/margin-mail`, bundle id in the same `studio.margin.*` family, FSL-1.1-MIT. Third in the suite
|
|
and the largest Rust codebase at 46,000 lines. No git remote yet.
|
|
|
|
The premise is a keyboard-first email client over Gmail where the user never feels Gmail. HEY and
|
|
Superhuman are the feature vocabulary. Mailspring is the reliability bar.
|
|
|
|
Product decisions locked on 2026-09-03, not to be reopened unless the user does:
|
|
|
|
macOS first, then iOS, then minor work for Linux. List plus reading pane in the Superhuman shape,
|
|
with HEY's Reply Later and Set Aside piles, and Feed and Paper Trail as views. The Inbox is one list
|
|
in time order under Back, with unseen shown by weight alone: no dot, no band, no groups. Seen on open
|
|
at once, a new reply makes a thread unseen again, the dock badge stays, and there are no counts in
|
|
the list. The Screener is on by default with a first-run pass that screens in anyone who has emailed
|
|
before. No AI in v1, though the design leaves room. Trackers are blocked and never sent, because
|
|
privacy and ownership are core tenets. Scheduled sending is skipped; snooze and Bubble Up stay,
|
|
evaluated lazily when a device opens or wakes. Multiple accounts with per-account views and an
|
|
optional unified view. Backends after Gmail are generic IMAP and SMTP, then JMAP for Fastmail.
|
|
Calendar invites RSVP inline and hand off to Margin Calendar, with no calendar sidebar. Compose is
|
|
inline at the end of the thread, or a floating card for new mail. Full local mirror of mail, with
|
|
attachments on demand.
|
|
|
|
App state must not depend on Gmail or any provider. In the user's words: "if I switch to protonmail
|
|
tomorrow I don't want to lose data." Local data is the truth and cloud stores are backup only, behind
|
|
one interface, with Google Drive for non-technical friends and Cloudflare R2 for the user. Key
|
|
portable state on the RFC Message-ID and the sender address, never on provider ids.
|
|
|
|
Four Playwright specs fail on a clean tree and are not flakes. `tests/shell.spec.ts` ("New for you
|
|
above Previously seen"), `tests/snooze.spec.ts` ("Back above New for you") and `tests/triage.spec.ts`
|
|
("Mark all as seen is a link on the heading") all assert Inbox group heads that the app deliberately
|
|
no longer draws: `GROUPS` in `src/ipc.ts` has no `new` or `seen` label and `ListColumn` renders
|
|
`GroupHead` with no action. The fourth, `tests/kit.spec.ts` ("every text field tells the webview not
|
|
to fill it in"), is a static scan that flags an input in `Kit.tsx` and one in `Settings.tsx`. Match on
|
|
the test name, not the line number. If exactly these fail, say they are pre-existing and move on.
|
|
Fixing them means rewriting the assertions to the current design, which is its own piece of work.
|