Commit Graph
996 Commits
Author SHA1 Message Date
Thomas DurieuxandClaude Opus 5.5 511dcd7067 feat(admin): billing tab to monitor subscriptions and sales (#952)
* feat(admin): add a Billing tab for subscriptions and sales

The admin can now follow subscriptions and sales without opening Stripe:
- Summary tiles: active subscriptions (monthly/annual), MRR with annual
  plans counted as 1/12, sales and revenue for 7d/30d/90d/12m/all time,
  all-time sales, subscriptions cancelling at period end, and refunds
  and disputes.
- A stacked chart of sales per day, week or month by plan family.
- A by-plan table: active grants, period and all-time sales and revenue.
- A paginated list of grants with plan, Stripe status, active/expired/
  revoked and user filters, linked to the admin user, repository or
  conference.
- One row of webhook health: pending, failed, oldest pending event, last
  error and unreviewed payment reversals.

A sale is one checkout or subscription, so a paper pack's three grants
count once. Revenue is each sale's Stripe list price, resolved once per
price and cached in memory. Currencies are never added together. Without
Stripe the page shows counts and marks revenue as unavailable.

GET /api/admin/billing/summary and /grants sit behind the admin check.
Grants get a { purchasedAt, _id } index for the list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: preserve payment history in admin billing totals

* fix: scope billing history and preserve original sale prices

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 14:37:29 +01:00
Thomas DurieuxandClaude Opus 5.5 476f25e264 feat(ui): browse and check every file in the anonymize preview (#950)
* feat(ui): browse and check every file in the anonymize preview

The "What reviewers will see" window now redacts the README GitHub actually
shows and lets authors open any file of the commit.

- README: resolved from the file list like GitHub (.github/, root, docs/,
  any case, Markdown first) and only that path is tested against the
  "Leave files unredacted" rules. Before the list loads, only root names are
  assumed, so leaving docs/ unredacted no longer unredacts the root README.
  The file bar shows the README's real name.
- New GET /api/repo/:owner/:repo/file?commit=&path= (same access checks as
  /tree): raw text, no-store, path traversal rejected, 1 MB cap
  (file_too_big, 413), binary files reported as { binary: true }.
  /tree also lists binary files (binaryFiles) for the preview's tree.
- Tree rows open files (keyboard accessible, explorer active rail). Text is
  redacted through /api/anonymize-preview by the same batcher as the README;
  files left unredacted are sent without terms and tagged "Left unredacted"
  (opens the dialog). Markdown renders like the README, code is highlighted
  with Prism, images/PDF/binary/too-large files get a short note. Loading
  and error (Retry) states reuse rp-state.
- File and folder names are redacted through /api/anonymize-preview.
- Caption counts the open file; "<name> left unredacted" when preserved.
- Phones: the Files button opens the tree as a sheet.
- dev-mock: a jsmith/robust-repair source with tree, file and
  anonymize-preview endpoints and fixtures carrying the mock's terms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(ui): reuse the explorer in the anonymize preview

Share the explorer file renderer and tree with the author preview. Apply
preserved-file rules on the server and keep the reviewer rendering and
redaction slots consistent across both views.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ui): keep the reviewer page unchanged by the preview reuse

The preview reuses tree.js and the shared file viewer; its differences now
apply only inside the anonymize preview.

- The narrow-pane rule that hid the file meta and Symbols applied to the
  reviewer page too (visible with a widened sidebar); it is scoped to
  .reviewer-preview. The token capture moves from body to the page that
  holds the preview.
- tree.js: aria-expanded/aria-current and tree-only scrolling apply only
  when the tree is embedded (selectedPath set); the reviewer page keeps
  scrollIntoView and its original markup.
- file-viewer: the extra menu-closing click listener, the #L hash writes and
  the Ctrl+F/G and Alt+Z shortcuts are limited to the preview (shortcuts
  only while its code has focus), so the form keeps the browser's Find.
- Preview: the tree's file list is cached between renders, name batches are
  chunked under the JSON body limit, /api/anonymize-preview accepts up to
  3 MB so files up to the 1 MB preview limit can be redacted, and on phones
  the caption's "<name> left unredacted" opens the dialog (the header tag
  squeezed the path out).
- dev-mock: long and nested names for the tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ui): restore the redaction number on the right and level the slots

The redesign centered the term number in every redaction slot; it sits on
the right edge again, in code, Markdown, the tree and the breadcrumb. Tree,
breadcrumb and paragraph slots also sat about 2px below the text; they now
center on the line's letters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: address preview navigation and path review findings

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 14:27:13 +01:00
Thomas Durieux d508ab1754 fix: bound metadata work and distinguish cancelled requests (#951) 2026-11-13 11:38:26 +01:00
Thomas Durieux ed07ecde73 Merge pull request #949 from tdurieux/fix/ui-followups
fix(ui): header wrapping, preview file strip, clipped Create button and paid auto-update
2026-11-13 05:06:37 +01:00
Thomas DurieuxandClaude Opus 5.5 710756fd02 fix(ui): open the preview tree at the top, like the explorer
Auto-scrolling to README hid the first folders and cut the top row in half.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 03:59:06 +00:00
Thomas DurieuxandClaude Opus 5.5 6226acff80 fix(ui): show a vertical file tree in the reviewer preview
The "What reviewers will see" window now has a sidebar like the explorer's
instead of the one-row file strip: folders first, then files, with the
explorer's tree rows, icons and teal selection on the README. Folders expand
from the loaded file list. The sidebar is 24% of the window (180-240px),
tinted full height, and scrolls apart from the document. Phones hide it and
show the explorer's "Files" button in the file bar. Loading shows skeleton
rows; without a file list the sidebar shows the README only.

Removes the strip fitter and the dead rail/strip CSS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 03:54:56 +00:00
Thomas DurieuxandClaude Opus 5.5 7423c78e27 fix(ui): follow-ups after the redesign
- Tabs: one indicator for every tab bar (admin, settings, dashboard,
  conference, PR/gist viewer, error detail, workspace phone tabs, home
  steps): a straight 2px accent line as wide as the label, on the bar's
  bottom rule. Hover changes only the label colour. The phone admin nav
  is one scrolling row.
- Header: nav labels never wrap. Signed in, "Report a bug" and "Support"
  move to the avatar menu below 1366px, gaps tighten below 1100px, and a
  fitter applies the same steps whenever the bar still overflows (wider
  fonts, Upgrade link, long usernames).
- Dashboard: 12px between the search row and the active filter chips;
  "Load more projects" sits 20px under the table, centred.
- Reviewer preview: the file strip is one row (folders first, README
  always shown, then "+N more") instead of wrapping every top-level entry.
- Workspace footer: the submit button keeps its width and never clips; the
  draft note beside it wraps. The bar uses the fields' 20px inset.
- Auto-update: without an allowance that includes automatic updates, the
  checkbox shows a Paid tag and the Live updates price, and the hosting
  section explains how to add it. #946's hosting message for this case
  could never show.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 03:38:30 +00:00
Thomas Durieux facfbc597b Merge pull request #948 from tdurieux/feat/ui-polish
feat(ui): one consistent design across author, reviewer and admin screens
2026-11-13 03:33:11 +01:00
Thomas Durieux 3408a6ced9 Merge pull request #946 from tdurieux/feat/one-time-billing
feat: let authors pay once per paper instead of subscribing
2026-11-13 03:32:52 +01:00
Thomas DurieuxandClaude Opus 5.5 c044b83541 feat(ui): one consistent design across author, reviewer and admin screens
The UI was uneven: the home page looked finished, but app pages used three
different display typefaces, the anonymize flow changed frame between steps,
and the dashboard, explorer and admin screens each had their own controls,
status colours and edge-case handling.

Make every screen follow one system: shared page header, grid, buttons,
filters and status colours; a repository picker and step trail on anonymize;
a "What reviewers will see" preview of the actual reviewer page; consistent
file headers and viewers for every file type; truthful admin charts and
tables. Fold the accumulated override blocks back into style.css (22%
smaller, no visual change).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 02:22:45 +00:00
Thomas DurieuxandClaude Opus 5.5 0f78182c58 fix: keep legacy prices and address review findings
Subscriptions sold at a replaced price lost their plan, because a
subscription was matched only against the configured price. Resolve
prices through the configured price, STRIPE_LEGACY_PRICES, then a
registry that now also records subscription prices and every configured
price at startup. Store the price on subscription grants.

Review fixes:
- Recover a Never expire add-on found by reconciliation before expiring.
- Open Checkout for the subscription cycle the hosting option shows.
- Report conference hosting only while the package payment stands.
- Keep automatic updates on a paid-slot repository while its owner subscribes.
- Apply the repository expiration limit only to repositories.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 17:13:19 +00:00
Thomas DurieuxandClaude Opus 5.5 4340a9fe09 docs: keep the €49 annual subscription
Six of the first 22 subscribers chose the annual plan at €49 and it
brought 79% of revenue, so cutting it to €30 would lose money.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 16:36:52 +00:00
Thomas DurieuxandClaude Opus 5.5 d25e5336ce feat: let authors pay once per paper instead of subscribing
Most authors use the service for one paper a few times a year, so a
subscription does not fit. Add cheap one-time options (Paper pack, Extra
repository, Live updates, Extended expiration, Never expire), cap free
dated expiration at 5 months, keep cheaper subscriptions for heavy users,
and make every new conference a paid review package in three sizes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 16:04:31 +00:00
Thomas DurieuxandClaude Opus 5.5 643f4b87e9 feat: let authors leave their bibliography unredacted (#941) (#943)
* feat: let owners leave chosen files unredacted (#941)

Redacting self-citations in a bibliography reveals which papers are the
authors' own. Owners can now pick files whose content is served as
written; file names are still redacted. Patterns follow .gitignore rules.

The form gets a file picker fed by GitHub's tree API, and the terms box
grows with its content and only warns about regex symbols that can stop a
term from matching.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: honour !patterns, keep picked braces literal, preview unredacted README

- Patterns now apply in order and the last match wins, as in .gitignore,
  so "*.bib" then "!private.bib" excludes private.bib instead of
  preserving every other file.
- Picked paths escape glob characters, braces included, with a
  backslash; "[{]" did not survive brace expansion.
- The README preview drops terms when the README is an unredacted file,
  and refreshes when the list changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep rule order when deduplicating unredacted-file patterns

Rules apply in order and the last match wins, so dedupe keeps the last
copy of a repeated pattern instead of the first. The picker also keeps a
plain path in its place when it follows another rule, rather than moving
it to the front as a pick.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: apply unredacted-file rules to folders, as in .gitignore

A rule that matches a folder now covers every file inside it, so "*.bib"
then "!private" keeps private/refs.bib redacted. A trailing "/" limits a
rule to folders.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: folder unchecks win over broad rules; accept short commit hashes

- Unchecking a folder that a broader rule such as "*.bib" still covers
  appends a "!/folder/" exclusion, so its files really go back to being
  redacted.
- The tree endpoint accepts the 6+ character hashes the commit field
  allows and resolves them to a full SHA before reading the tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: picker counts folder rules exactly as the server does

Rules ending in "/" stay patterns instead of being read as picked files,
and the checkboxes now run the saved rule list (picks, then patterns)
through the server's matcher, so the dialog cannot show files as
redacted that the server leaves unredacted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: insert escaped terms literally in "Match as written"

String.replace expanded "$&", "$'" and "$1" in the term, so the fix
could corrupt the line. Splice by index instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 14:10:54 +01:00
Thomas Durieux accfdeef50 Merge pull request #942 from tdurieux/fix/folder-count-index-selection
perf: fix repository scans and Mongo index selection
2026-11-11 11:29:11 +01:00
Codex 3999a71583 perf: index repository lookups and billing maintenance 2026-11-11 10:18:20 +00:00
Codex 6a230f73b9 perf: select the covering index for folder counts 2026-11-11 10:04:03 +00:00
Thomas Durieux a02568e265 Merge pull request #940 from tdurieux/perf/covered-folder-counts
perf: count folder files from a covering index
2026-11-11 10:53:59 +01:00
Codex 1505fd2baa perf: count folder files from a covering index 2026-11-11 09:05:30 +00:00
Thomas Durieux a6e5696746 Merge pull request #939 from tdurieux/fix/cached-files-without-github
perf: serve cached OAuth snapshots without GitHub calls
2026-11-10 13:59:05 +01:00
Codex 0abbe34aed fix: preserve streamer error semantics for cache probes 2026-11-10 12:55:55 +00:00
Codex 07458f536a fix: preserve cache fallback and stream failures during access checks 2026-11-10 12:51:09 +00:00
Codex 4fc3c2e042 perf: serve cached OAuth snapshots without GitHub calls 2026-11-10 12:03:52 +00:00
Thomas Durieux bb13346dd7 Merge pull request #937 from tdurieux/ops/limit-bulk-file-fetches
ops: limit bulk file fetching and repeated directory polling
2026-11-10 12:39:21 +01:00
Thomas Durieux 819906aeb5 Merge pull request #938 from tdurieux/fix/coalesce-github-credential-checks
perf: reduce GitHub quota spent validating credentials
2026-11-10 12:38:34 +01:00
Codex 5267599254 perf: reduce GitHub quota spent validating credentials 2026-11-10 11:36:55 +00:00
Codex b51e5afa4a ops: limit bulk file fetching and repeated directory polling 2026-11-10 11:31:55 +00:00
Thomas Durieux 25fe9d2df7 Merge pull request #936 from tdurieux/ops/attributable-edge-logs
ops: attribute edge traffic and correlate upstream failures
2026-11-10 09:54:11 +01:00
Codex 905b8ad781 ops: attribute edge traffic and correlate upstream failures 2026-11-10 08:46:57 +00:00
Thomas Durieux ff151d5246 Merge pull request #934 from tdurieux/fix/ui-table-menu-clip
fix(ui): stop row menus being clipped by the table container
2026-11-09 11:47:09 +01:00
Thomas Durieux 459d37f6d7 Merge pull request #935 from tdurieux/fix/reject-folder-file-downloads
fix: prevent folder downloads from exposing GitHub metadata
2026-11-09 11:46:04 +01:00
Codex 7c7caa845c fix: prevent folder downloads from exposing GitHub metadata 2026-11-09 10:45:24 +00:00
Thomas DurieuxandClaude Opus 5.5 b8a6e9ff89 fix(ui): stop row menus being clipped by the table container
.paper-table hid overflow to keep its rounded corners, which cut off
row ⋯ menus on short lists (admin repositories, My work). Let the
container overflow and put the corner radius on the head and last row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 10:42:35 +00:00
Thomas Durieux 4834289c8e Merge pull request #933 from tdurieux/fix/ui-expiry-control
fix(ui): go-to-line popover, selection overlap, copy icon, date and checkout layout
2026-11-09 11:35:14 +01:00
Thomas DurieuxandClaude Opus 5.5 2600bfa2f6 fix(ui): go-to-line popover, selection overlap, copy icon, date and checkout layout
- Go to line: themed popover under the file toolbar (line or line:column,
  range check) instead of Ace's unthemed prompt
- Code selection: no per-box outline, so multi-line selections no longer
  overlap at the joins; single-line matches keep an inset outline
- My work: copy-link is an icon button (URL in the tooltip); phone cards
  drop the URL row and truncate long names
- Date fields: drop WebKit's native button styling so they fit their column
- Submit bar: the checkout note gets its own line under the button

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 10:33:06 +00:00
Thomas Durieux aaf47da4dc Merge pull request #932 from tdurieux/fix/ui-admin-mobile
fix(ui): admin errors filters, overview views, phone menu, ended projects
2026-11-09 10:19:37 +01:00
Thomas DurieuxandClaude Opus 5.5 9ba9af2b44 fix(ui): admin errors filters, overview views, phone menu, ended projects
- Admin errors: filters stack on phones (tabs, search, Sort and Group,
  refresh) instead of squeezing into one row
- Admin overview: Summary / Performance / Cache views, kept in the URL
  hash; charts take the full width on phones
- Phone menu: aligned rows with icons, neutral fill for the current page,
  account links inline, no duplicate Sign in
- My work: removed and expired projects get a labelled badge, a muted
  "Link offline" in place of the copy link, and an inline Enable/Extend

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 09:18:25 +00:00
Thomas Durieux ebcd880f4a Merge pull request #931 from tdurieux/fix/bounded-zip-finalization
fix: bound ZIP directory memory for slow downloads
2026-11-09 10:16:05 +01:00
Codex ac28c64c6c fix: bound ZIP directory memory for slow downloads 2026-11-09 08:54:26 +00:00
Thomas Durieux 4263a87399 Merge pull request #930 from tdurieux/fix/persistent-login
fix: persist login sessions across browser restarts
2026-11-08 21:51:34 +01:00
Codex 6593319c87 fix: persist login sessions across browser restarts 2026-11-08 20:32:03 +00:00
Thomas Durieux ba3382b1d7 Merge pull request #929 from tdurieux/fix/ui-mobile-bugs
fix(ui): mobile viewer, code-block redactions and anonymize preview
2026-11-08 21:30:27 +01:00
Thomas DurieuxandClaude Opus 5.5 5bad2a2597 fix(ui): mobile viewer, code-block redactions and anonymize preview
- Anonymize preview renders redactions as the viewer's numbered slots
- Code-block slots keep the width of XXXX-n and sit on the text line
- README no longer overflows on phones (grid forced two columns)
- Status bar always hides under the file header: no second Files button
  or breadcrumb on repos with a website or for owners; Open website and
  Edit anonymization move to the file menu
- Loading state no longer repeats the path visually
- Phone usage popover opens inside the screen
- Home closing band: no double divider, tighter gap above the footer
- File drawer closes after opening a file on phones

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 20:29:50 +00:00
Thomas Durieux 91f57879fb Merge pull request #928 from tdurieux/fix/restore-node-main
fix: restore Node deployment and keep Go on its branch
2026-11-08 20:21:06 +01:00
Codex bff2e343fd fix: restore Node deployment and keep Go on its branch 2026-11-08 19:16:38 +00:00
Thomas Durieux 1983441c68 Merge pull request #927 from tdurieux/feat/ui-polish-2
feat(ui): polish from design review rounds 92-121
2026-11-08 19:17:29 +01:00
Thomas DurieuxandClaude Opus 5.5 9872cace5e feat(ui): polish from design review rounds 92-121
- Phone viewer: Contents menu for README headings, theme toggle in the header,
  GitHub icon on sign-in, 12px Ace font
- File ⋯ menu: file details line, Find for HTML, View raw, Download ZIP,
  Copy file path
- Redaction slots inside highlighted code blocks; fix regex lastIndex reuse
  in the redaction walker
- Landing and FAQ copy: plainer headings, trust items, closing CTA band
- Explorer ZIP button label, snapshot text cleanup

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 18:14:47 +00:00
Thomas Durieux c63d153a5f Merge pull request #926 from tdurieux/feat/ui-redesign
feat(ui): redesign the reviewer viewer, landing, author and admin screens
2026-11-08 15:08:23 +01:00
Thomas DurieuxandClaude Opus 5.5 c685d833ab Merge origin/main into feat/ui-redesign
Backend, scripts, docs and their tests take main's newer code where both
sides changed the same lines (the shared blob cache, GitHubStream and
markdown fixes landed on main after the Go migration branch was cut).
The Dockerfile keeps the Go build from the migration, plus main's
owner-alert-protocol label; .gitignore keeps both sides' entries.

Frontend keeps the redesign and folds in main's features: Stripe billing
in the New anonymization submit bar, GitHub access repair, redaction
syntax help, the preview expand toggle, configurable redaction mask,
folder counts, reviewer-route header rules, link expiry (now in the
viewer's snapshot details) and the explorer error card. main's
narrow-only wrap logic is dropped because the redesign wraps on desktop
and scrolls sideways on phones.

Tests: 1534 passing with SESSION_SECRET set (as in CI). Three main tests
were updated for the agreed viewer design (no site header in the viewer,
no repository-wide file count).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 14:07:33 +00:00
Thomas DurieuxandClaude Opus 5.5 53cac6ac39 feat(ui): follow-up polish from design review rounds 88-91
One-row phone toolbar, errors page opens on server errors, one-row submit
bar, publish warning after a source loads, three-column FAQ with a help
card, queue range control in the Throughput card, shared admin table cards,
dark-mode contrast and a stronger phone code scroll cue.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 13:36:59 +00:00