-
Notifications
You must be signed in to change notification settings - Fork 0
Documents
Everything opens where it is, rather than being downloaded first.
-
Markdown rendered, with a toggle to see the source — and the figures next to it on disk are shown:
resolves against the document's own folder and is fetched like any other file, so the jail still decides what can be read. -
```mermaid diagrams drawn, not left as three lines of arrows in a grey box — see below.
-
PDF drawn by Argus itself, so it opens where you left it and page width means page width — see below. A finder folds into a button over the page: the browser's Ctrl+F searches the page around a document, and a phone has no Ctrl+F at all.
-
Video and audio, played in the window rather than downloaded. The server streams the file and answers a range request, so the scrubber works from the first second instead of the recording having to arrive whole before anything can be seen, and the preview cap does not apply — a recording is bigger than any cap worth having.
It keeps your place: where you had got to is remembered per file and survives a reload, which is the moment it is wanted, since a window watching a recording that is still being written reloads on its own. The last forty files, oldest dropped.
What plays is what the browser can decode: mp4 and webm everywhere, mkv and mov usually not. A container it cannot decode says so and offers the download, rather than showing a black rectangle and leaving you to guess.
-
Word, ODT, RTF through pandoc when the machine has it — headings, lists, tables, links, and figures inlined — with the plain-text extraction as the fallback when it does not.
-
Excel —
.xlsxand.xlsm— as a real HTML table, one per sheet, read directly rather than through pandoc — see below. -
Images inline. Logs and any text file too large to send whole arrive as their tail, which is the part anyone wants, and the viewer says so and starts at the end.
Every rendered document is served under a CSP sandbox, in an opaque origin: a report
written by somebody else must not be able to read the access token. Markdown is escaped
before rendering and javascript: links are stripped.
Saving from the viewer keeps the real name. Files served to be read rather than saved
carry Content-Disposition: inline with their filename, or the browser's own save button
names every download after the URL — which is how everything used to arrive as file.pdf.
Behind the i — the same one a terminal wears, meaning the same thing — a strip says which
folder the document is in and when it last changed, to the minute. The listing says
18:04 for today and 25 Aug for anything older, which is right in a column of forty files
and wrong here: a document being rewritten while you read it is one whose time you want, and
25 Aug does not tell you whether the run that was meant to update it has finished. Its size
is in the tooltip, which is the other half of that question. Hidden until you ask, because most of the time you already know, and
it is one answer for every document rather than a state per window. Where a document has
controls of its own, the strip goes under them: a PDF's page and zoom buttons stay the first
row. The path opens Files there, one button puts a file browser on that folder in the desk
you are on, and two copy: one a link to the file — its http address, with the token, the
thing you paste into a browser or a message — and one its absolute path, the thing you paste
into a terminal. Both wear the tick every other copy in this app gives you.
Two buttons because they are two different answers. For a PDF especially, what is useful to hand somebody is a link that opens the document, not a filename they cannot reach.
It is there because the header says the file's name and nothing else, which is right until the moment you want to see what is beside it or paste the path into a terminal — and both of those were three moves away.
A .mmd or .mermaid file — what the mermaid command line reads and writes — is drawn rather
than shown as a wall of arrows, which is the one case where a reader is worse off than running
cat. It is the same engine that draws fenced diagrams inside a document, so it costs nothing
until the first one is opened.
The colours are a choice, in Settings. auto follows the app — which is what any other
mermaid tool does, and which on a dark page means mermaid's dark theme, greys. The colourful
one everybody recognises from mermaid.live is default, offered here as colourful, and it
can be chosen against a dark app because wanting a colourful diagram on a dark page is not a
contradiction. forest is greens, flat is the old behaviour: the app's own two greys and
one green, restrained inside a document and dull on a diagram you are working on.
Whichever is chosen, a diagram's own classDef and style lines win. The theme decides what
an unstyled node looks like, never what a styled one does.
<> switches to the source, as it does for markdown. And a diagram that will not parse keeps
its source and says what mermaid objected to, with the line number: half-written is the
normal state of a diagram you are working on, and an empty box would hide the very thing you
need to fix.
An .stl file is drawn as the shape it describes, orbitable with a drag — the file itself is
just a list of triangles, and a wall of facet normal … vertex … is the same "worse off than
cat" case a bare mermaid diagram used to be. Binary or ASCII, without being told which,
because the format itself does not say either.
Faceted on purpose. Each triangle's normal is computed from its own geometry rather than trusted from the file, which is both the conventional look for a printed or milled part — edges, not a smooth blend across them — and the safer choice: a bad or absent normal in the file is a real thing that happens, and a computed one cannot be wrong in the same way.
Wears this app's palette, the same bargain a mermaid diagram already made: the model's colour and the space around it are the accent and the background, and both follow a theme switch rather than staying whatever they were drawn in.
Loaded only when a document of this kind is actually opened — three.js is by far the largest thing vendored for a viewer here, and the overwhelming majority of documents are not meshes.
.step and .stp open the same way — orbitable, faceted, in this app's own colours — but
they are a different kind of file underneath, and the difference is worth knowing.
An STL is triangles; a STEP file is a boundary representation: curved surfaces described
algebraically, the way the CAD program that wrote it actually thinks about the part, plus —
often — several parts in one file, each placed where they belong in the assembly. Turning
that into triangles for a screen is a CAD kernel's job, and this uses a real one:
OCCT, the engine behind FreeCAD and much else, compiled to
WebAssembly by occt-import-js and loaded only
when a .step is actually opened — 7.5 MB, by far the largest thing vendored here, next to
which three.js itself is small.
Several parts stay assembled. Centring a multi-part file the way a single STL is centred — each part pulled to the origin on its own — would scatter the assembly into a pile of disconnected shapes sitting on top of each other. Every part in the file is offset by the same amount instead, computed from the bounding box of the whole assembly, so a bolt through two plates is still a bolt through two plates.
Colour is this app's, not the file's. A STEP file can carry a colour per part, sometimes per face — the same choice binary STL's extended colour byte makes, and the same answer: ignored, in favour of the one palette used everywhere else in here. Shape is what the file is asked for; colour is asked of the theme.
occt-import-js and OCCT itself are LGPL-2.1, not MIT like everything else vendored
here — noted in static/vendor/occt-import-js-*/NOTICE.md, along with why loading it as a
plain fetched file rather than bundling it into app.js already satisfies what that licence
asks for.
.xlsx and .xlsm render as a real <table>, one per sheet, in the workbook's own tab
order — not through pandoc, even though pandoc lists xlsx as a reader it can open.
It was tried first, and it fails on real files. A worksheet's location is recorded in
workbook.xml.rels as a Target, and that target is legally written two ways: relative
to the package, or — what real Excel actually writes — package-absolute, starting with
a /. Every .xlsx this was tested against, including several already sitting in this
machine's own home directory, uses the absolute form, and pandoc's reader resolves it
wrong: /xl/worksheets/sheet1.xml comes back as xl//xl/worksheets/sheet1.xml, an entry
that does not exist in the archive, and the whole file fails to open. A grid has no real
use for pandoc's document model regardless — a table is what a sheet already is — so this
reads the same zipped XML the no-pandoc .docx fallback already reads: workbook.xml
for the sheet names and order, .rels for where each one actually lives (both target
forms), sharedStrings.xml for the text most cells reference rather than repeat.
No dependency, the same trade the text extractor already makes. Nothing is vendored
for this, on the server or in the browser — the same "standard library, formatting lost"
bargain a .docx without pandoc already accepts, kept as a grid instead of flattened to
lines, because a table stripped of its columns is not a smaller table, it is a different
and much less useful thing. Numbers and dates come out as whatever was actually stored —
a date is a serial number in a cell with no calendar attached to it without also reading
the style table, and that is not done here — the same kind of loss .docx already accepts
for the sake of needing nothing else installed.
Older .xls — the pre-2007 binary format, not a zip at all — is not read. It refuses the
same way any other unreadable binary file does: a plain error, and the download still
works.
In a window — not on the full screen, where there is nothing to follow with — a document wears two buttons. The circular arrow reads it again now, whatever the disk says. The eye beside it is the switch: while it is on, the file is checked every three seconds and reloaded when it changes, which is what you want in front of a report a job is regenerating.
A PDF reloads with the rest, and comes back to the page, the scroll and the zoom you were on. It used to offer instead — this file has changed, [Reload] — on the reasoning that a document rebuilt while you read page thirty must not throw you to page one. That stopped being true when this viewer learned to remember the place, and what was left was a notice standing between you and the new version you had already asked to see.
A reload that lands mid-write draws a broken document for one tick and then fixes itself: a file still being written keeps changing, so the next check reloads it again, and the last reload is the finished one.
A file of code opens coloured — 27 languages, chosen from the name — and the highlighter is loaded the first time you open one, never before: somebody who reads logs and markdown all day pays nothing for it, and the app's first paint is unchanged. It is vendored like everything else here, so nothing reaches a CDN.
Two limits, both deliberate. Above 260KB of text the file stays plain: highlighting it takes longer than reading it does, and a frozen tab is worse than grey code. And the colours are this app's, not a theme downloaded with the library — a borrowed theme brings its own background and its own greens, and two palettes in one window is a collage.
A fenced block whose language is mermaid is a diagram everywhere else it is read — GitHub,
GitLab, the editor it was written in — and here it used to be its own source in a grey box,
which is the one place a reader was worse off than running cat on the file. Now it is
drawn: flowcharts, sequences, state, class, ER, gantt, pie, git graphs, and the rest of what
mermaid 11 knows.
Three things about how:
- It wears this app's palette, not mermaid's own dark and light themes: the same two greens, the same greys, the same lines as the document around it. And it follows a theme switch — the colours are written into the drawing rather than read from a variable, so changing theme redraws every diagram on screen rather than leaving a black box on a white page.
-
Loaded only when a document actually has one. It is by far the largest thing vendored
here, and the overwhelming majority of documents are not diagrams. Nothing reaches a CDN,
as with everything else in
static/vendor/. - A diagram that will not parse stays as its source, with one line under it saying what the parser objected to. Hiding it would lose the very text you are trying to fix, and mermaid's own red error box is not put on the page.
Labels go through mermaid's sanitiser (securityLevel: strict): no scripts, and no click
handlers, in a document somebody else wrote.
The guess is: markdown rendered, code coloured, everything else plain. It is right nearly
always, and wrong exactly where it matters to the person it is wrong for — a .log that is
really JSON, a .txt that is a config, a .md you want to read as source because it is a
template.
So it is only a default. Settings → Documents → Which viewer, by extension takes a row per extension you disagree about:
auto |
let Argus decide, which is the behaviour above |
code |
colour it, whatever it is called |
plain |
text, and nothing else |
markdown |
render it |
An empty row waits at the bottom: adding one is typing, not finding a button. The ✕ forgets
a row and puts that extension back to auto.
Which viewer, by extension above is a preference, kept per device, for the text
viewers only. viewers in config.yaml is its server-side counterpart for everything
else this wiki has described — PDF, mesh, spreadsheet, and the pandoc/zipped-XML
documents — and it answers two different questions.
How big is too big, for this kind of file. max_preview_bytes is one number for
everything by default, and that stopped being enough the day a real thesis PDF, a
legitimate 10+ MB document, ran into the same cap meant to stop a phone from being
handed a runaway image. viewers.max_bytes overrides it per kind:
viewers:
max_bytes:
pdf: 31457280 # 30 MiB — this lab's PDFs run bigger than most
default: 2097152 # everything not named here, same as max_preview_bytes todayKinds: pdf, mesh (.stl/.step/.stp, one cap for both), spreadsheet
(.xlsx/.xlsm), document (Word/ODT/RTF/EPUB — pandoc or the no-pandoc fallback),
image, and default for text and anything unrecognised. A kind with no entry falls
back to max_bytes.default if that is set, and to max_preview_bytes if it is not —
existing configs are unaffected by any of this until viewers is actually written.
What an extension actually is, when its name disagrees with its content — a report generated with the wrong extension, a spreadsheet export nobody thought to rename:
viewers:
force:
dat: pdf # every .dat here is really a PDF
rpt: spreadsheet # every .rpt here is really an .xlsxpdf, stl, step, spreadsheet, or text — the last one for the opposite mistake, an
extension that would otherwise be sent through the document or spreadsheet renderer but
is actually plain text. It is not a promise to render binary data as text regardless: a
file that is genuinely binary is still refused, the same 415 an unrecognised binary file
always gets. Forcing only stops the guess from a name being wrong, not the safety check
on what is actually in the file.
Neither half applies to a file opened with Ctrl/Cmd+click — see Sessions and the terminal — which already means the real file, no interpretation, cap or otherwise.
Argus draws PDFs itself, with pdf.js, rather than handing them to the browser's viewer. That is not a preference; it is the only way to answer the questions people actually ask of a document they are reading.
The browser's viewer will not talk to the page around it. Measured: it answers
documentLoaded and getSelectedText, and says nothing at all while you scroll — so
where you had got to was not something that could be recorded, let alone put back. It also
honours view=Fit while silently dropping view=FitH, which is why page width never
survived a reload, for months, and had to be set again every single time.
So, now:
- It opens where you left it. The page, the scroll position to the pixel, and the zoom you chose. Across a reload, across F5, across closing the tab and coming back tomorrow — it is kept in the preferences, for the last sixty documents.
- Page width means page width, because Argus computes it rather than asking for it. Whole page and as-it-comes are the other two; the button in the bar cycles them, and Settings says which a document you have never opened starts at.
- The page number is a box you type in, with a step either side of it: put 31 in, press Enter, and you are on page 31. The steps grey out at the ends, and the box does not rewrite itself under your fingers while you are typing in it.
- − and + for a zoom of your own, which then stays yours: resizing the window re-fits only while a fit is what you asked for.
- Out to a browser tab, for the things the browser does and this does not: printing, its own find, handing the file to something else. The fit you are on goes with it.
- The text is selectable, laid invisibly over the drawing, so a citation can be copied out of a paper.
- Only what is on screen is drawn, a few pages either side. A 53-page paper at page width would be some 200 MB of canvas if every page were kept, which a phone has not got.
The finder sits in the bar, and its panel hangs under it: Argus extracts the text on the
server (pdftotext), lists every hit with its page and context, and scrolls to the page
you pick rather than reloading the document to get there.
Leaving the desk and coming back keeps your place too. A desk that is not on screen is
display: none, so everything inside it measures zero — which is how the reader used to
be thrown to the end of the document every single time you looked at something else.
When the file is rebuilt while you are reading it — latexmk, a report an agent regenerates — the viewer still does not reload under you. It offers:
This file has changed. [ Reload ] ✕
The difference is that reloading is no longer a punishment: it puts you back where you were reading.
If pdf.js cannot open a document at all, the browser's own viewer is used for it instead, with a line saying so. Nobody is left looking at nothing.