Skip to content

OpenFiles App - #100

Merged
aaronsmulktis merged 39 commits into
mainfrom
aaronsmulktis/files
Oct 29, 2026
Merged

aaronsmulktis merged 39 commits into
mainfrom
aaronsmulktis/files

Conversation

@aaronsmulktis

Copy link
Copy Markdown
Contributor
Screenshot 2026-10-26 at 3 22 03 PM Screenshot 2026-10-26 at 3 22 25 PM Screenshot 2026-10-26 at 3 22 11 PM

Changes:

  1. Adds a Files app shared across apps. Bank statements and shop receipts are saved into it as real PDFs, and its Code folder is the code editor's workspace.
  2. PDFs are viewed with newly vendored pdf.js
  3. Recently Deleted keeps items for 30 days
  4. Files tasks (9 tasks × 3 phrasings), kept out of all_tasks. Purchase tasks now expect a PDF receipt in OpenFiles.
  5. Raw files are served safely on the shared origin.

… dates to the world's calendar, make answers commit to one amount, credit core-js
@aaronsmulktis aaronsmulktis self-assigned this Oct 26, 2026
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Oct 26, 2026
@aaronsmulktis
aaronsmulktis marked this pull request as ready for review October 26, 2026 20:40
@aaronsmulktis
aaronsmulktis requested review from marksibrahim and a balanced review from Copilot October 27, 2026 14:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

…s open on click

Receipt and statement links pointed at raw PDFs, which Playwright's Chromium
(the preview window, and every agent) shows as a blank tab plus a silent
download. They now open /files/pdf, the pdf.js reader for any same-origin
PDF. Search results linked a select into the root folder, where the file
isn't, so a single click went nowhere; they open the file now. The search
field drops Pico's pill radius to match the toolbar.
Stopping above the dock cut letter pages off at about two thirds on
landscape laptops. The viewer's top is measured (it moves with the theme's
type) and it fills the rest of the window, with the page around it held
still: one scrollbar, inside the document, like Preview. Page navigation
scrolls the well directly, since scrollIntoView would drag the
overflow-hidden page with it.
Orders sat at Processing forever and were stamped with the wall clock while
their receipt and card charge used the bank's as_of_date, so a receipt
predated its order. A new order now ships for the whole episode (a status
that moved with real time would make purchase rewards depend on episode
length) and takes Files' pinned day with the real time of day. The purchase
target expects Shipping: the reward compares status, contrary to the old
comment.
…us in the sidebar

Built-in folders offered Open only. They now offer Get Info and, except
Code (the editor's workspace, which lives outside the store and would be
copied out of it), Duplicate. Rename, move and delete stay blocked: tasks
address those folders by path. Get Info is on every item's menu, a read-only
sheet dated on the pinned calendar (a folder by its newest file, not the
directory's real-clock mtime). Sidebar folders open the same menu on
right-click, as in Finder.
Name took every spare pixel and pushed the facts to the far edge, or the
facts' fixed widths squeezed Name. Columns now open fitted to their
contents, so Name shows as much of each filename as the window allows, and
resize like Finder's: drag a header divider to trade width between its
neighbours, double-click to fit (Name's divider fits all the others),
arrow keys and Enter from the keyboard. Widths are kept per view in
localStorage only, never app state, so agents always see the fitted
default. The phone layout keeps automatic widths and no dividers.
…wer and columns

Every tracked variation gains files, files_pdf, files_trash and
openbanking_statements; existing routes change because OpenFiles is now in
the dock. Files pages show the fitted list columns and the full-height PDF
viewer.
Truncating every list cell (overflow: hidden, for the resizable columns)
also clipped the ... dropdown, which hangs below its row, so every row's
menu opened to nothing. Right-click still worked because that menu is
position: fixed.
Pico gives an open <details> summary a bottom margin to space it from its
content. A Files menu's content floats, so the margin only grew the row
(39 -> 59 px) while the menu was open; the sort menu did the same.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

The only non-default Files layout had no reference images; the other apps'
alternate layouts are tracked. All 20 routes, current UI.

@marksibrahim marksibrahim left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few changes requested, once those are addressed merge away!

@@ -0,0 +1,400 @@
"""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aaronsmulktis does this file just create the files? If so, I propose removing this to keep the main codebase simple and just keeping the generated files saved.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly. The seed docs are now committed files in files_app/seed/ (with a README) and
documents.py is now gone. Receipts and statements are still drawn at save time from order/ledger data, so
those two layouts moved to onlineshop_app/receipts.py and openbanking_app/statements.py. Every pack's /files_all and every statement/receipt is byte-identical to before.

Comment thread src/open_apps/apps/files_app/main.py Outdated
restoreColumns();
document.addEventListener('htmx:afterSettle', restoreColumns);
})();
""")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove this huge style string in its own file (that we import) to make this main.py more readable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CSS is in files_app/styles.py and the JS in assets/js/files_browser.js. main.py
went from about 1,890 to 1,260 lines.

Comment thread src/open_apps/tasks/tasks.py Resolved
Comment thread src/open_apps/tasks/tasks.py Resolved
Comment thread src/open_apps/apps/files_app/main.py Outdated
"""A PDF another app serves, in the Files reader (see ``pdf_document``).
Same-origin paths only: this page fetches ``src`` with pdf.js, and an
absolute or protocol-relative URL would turn it into a reader for anything."""
if not src.startswith("/") or src.startswith("//"):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't catch src=/\evil.com/x.pdf: browsers treat the backslash as a slash, so pdf.js and the Download link resolve it to //evil.com/..., which is the cross-origin case this check is meant to block. Suggest also rejecting \ (or parsing with urllib.parse.urlsplit and requiring empty scheme/netloc after normalising backslashes).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^Claude

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See next comment

Comment thread src/open_apps/files_store.py Resolved
: [...ZOOM_STEPS].reverse().find((s) => s < scale - 0.001);
if (next === undefined) { return; }
scale = next;
render().then(() => goTo(current));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

zoom() starts a new render() without waiting for or cancelling the one already running. Two quick zoom clicks start two render loops on the same canvases; pdf.js rejects that ("Cannot use the same canvas during multiple render() operations"), and nothing handles the rejection. Pages can end up at mixed scales, with the text layer and zoom label out of sync. Suggest keeping the in-flight RenderTasks and calling .cancel() on them first, or serialising renders with a pending-scale flag.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^Claude

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renders run one at a time, and a zoom mid-pass restarts at the new scale. Claude reproduced it in Chromium on a 30-page PDF: the old JS throws "Cannot use the same canvas…", the new JS renders every page at one scale with no errors.

…ments.py

Receipts and statements are still drawn at save time, so their layouts move to
onlineshop_app/receipts.py and openbanking_app/statements.py, sharing the
letterhead pieces now in pdf_writer. Every pack seeds byte-identical files;
the adversarial pack's note becomes text so it carries the run's message.
@aaronsmulktis

Copy link
Copy Markdown
Contributor Author

Two behavior changes you should know about @marksibrahim :

  1. The adversarial pack's care-team note is now .txt instead of PDF, so it can still carry whatever apps.adversarial_message a run sets.
  2. A new .gitattributes marks the seed PDFs binary. Without it, a Windows checkout with autocrlf would rewrite their line endings and break the PDFs.

@aaronsmulktis
aaronsmulktis merged commit 6535fd6 into main Oct 29, 2026
6 checks passed
aaronsmulktis added a commit that referenced this pull request Oct 30, 2026
…, docs (#101), parallel sweeps (#105)

Files and Contacts each registered a new app in the same places; both are kept,
Files at tile position 9 and Contacts at 10. AppStateComparison checks contact
handles, then files, exactly before the fuzzy diff. Restores the icons import
in the bank app, which the merge dropped while Files' Statements page uses it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants