{{/* show — GET /d/{sqid}, the document read view (spec §6.1/§6.2).
handlers.go's show builds the view data: Title is the document's
own title, printed as-is the same way list.html's row titles are
(data, not authored copy — not resolved through rastrillo.T);
HTML is Store.HTML's server-rendered replay of the CRDT's
ProseMirror fragment through wazero, never a database column, and
is empty for a document with no updates yet — an ordinary empty
canvas below, not an error. It is template.HTML, so {{.HTML}}
below is written out unescaped on purpose — see Store.HTML's own
doc comment for where that trust decision is made and why.
[docs-canvas]/[docs-page]/[docs-text] are this app's own
vocabulary (SKILL.md §7: "ui is rastrillo's vocabulary, not
yours"), not rastrillo's, so they are not spelled rst-*, same
reasoning as list.html's [docs-thumb].
The canvas opts out of the surrounding theme on purpose (spec
§10.2, the brief's canvas constraint): a document is a piece of
paper, and paper does not follow the reader's dark-mode
preference the way the chrome around it does. color-scheme:
light plus literal colours below — never var(--rst-surface) or
var(--rst-text), which would make the "page" repaint itself dark
along with everything else on screen.
[docs-page] is one page-shaped container, and it stays one in both
view modes. Paged draws desk-coloured gaps BETWEEN pages as widget
decorations inside the text flow (fixtures/pages.mjs) rather than
repeating this element.
This comment used to say a later version would repeat [docs-page]
once per page. It cannot: ProseMirror owns everything inside the
editable element ({mount: el}), so its children cannot be wrapped
in sibling page boxes without taking the document away from it.
Repeating the box is a layout-engine shape; a decoration is a
pagination-as-a-view shape, and a view is all pagination can be
while page breaks are computed from measured heights and never
persisted.
Writes the bare rst-* attribute grammar throughout — see
list.html's own comment on why, and
render_test.go's TestOurTemplatesUseTheAttributeGrammar, which
holds it against this file too. */}}
{{/* The bar is the layout's (views/layout.html, chrome.go). On this
screen it carries the document's title, editable in place for anyone
who may write (estilo's title slot, POST /d/{sqid}/title), and the
owner's "Share…" action. Sign out is in the account menu and nowhere
else. */}}
{{define "content"}}
{{/* This page has no [rst-page-header]. It used to, and what it held
is not gone — every piece of it moved to where its equivalent
lives in the two applications this layout language comes from.
What follows is only the map.
The route back to the list was [docs-back], a breadcrumb above
the title, and is now the bar's brand link, "Docs" — the same
control on every screen rather than one this page draws for
itself. Until [docs-back] landed this page had no in-app
navigation at all, so arriving by a bookmark or a mailed link left
a person with no route to their own list.
The title is in the bar beside the brand, editable in place.
Sign-out (POST /signout, auth.go) is in the bar's account menu and
nowhere else: it used to be the last item of File as well.
The exports are items in the File menu below. They used to be two
ghost buttons pinned to the formatting bar's trailing end, which is
what Paul's brief called the "random buttons": export is a
document-level verb, and the formatting bar is a group of run-level
controls, so it was the one place a person would not look. They are
still ordinary links to ordinary URLs, so both still work with no
JavaScript at all — spec §6.2 — and both are GETs of a document the
signed-in person may already read, so neither needs a form or a CSRF
token. download tells the browser to save rather than navigate; the
filename it uses comes from the server's Content-Disposition, not
from this attribute, because a title is arbitrary text a person
typed and sanitising it belongs server-side (handlers.go's
exportFilename). */}}
{{/* The menu bar (internal/docs/menu.go for the model and the copy,
fixtures/menu.mjs for the keyboard behaviour).
Each menu is a , and that is the load-bearing decision on
this screen rather than a convenience. The export links work today
with scripts disabled and the whole read view is built on that
guarantee (editor_view_test.go's TestShowRendersContentWithoutScript);
a JS-driven role="menubar" would have moved the only route to a
person's own document behind a control that never opens for them.
opens with no script at all, and name="estilo-menus"
makes the five an exclusive group — opening one closes the rest —
with no state for anything to keep. It is the SAME group as the
bar's account menu, switcher and Menu sheet, so opening File closes
the account menu and the other way round (spec §7.1).
It stays a disclosure rather than being relabelled role="menubar"
with role="menuitem" children. is a real disclosure
control that browsers and assistive technology already handle
correctly; declaring the menu pattern promises typeahead and
submenu semantics, and delivering less of it than that is worse for
a screen reader user than the honest disclosure it replaced.
fixtures/menu.mjs only ADDS to this: arrow keys between and within
menus, Escape to close, and a roving tabindex so the bar is one tab
stop. With no JavaScript every one of these still opens, and every
item that is a link or a form still works.
There are no submenus anywhere, and there will not be: a nested
inside a name= group collapses its parent (rastrillo's
rst-menus trap, which the Sheets session hit first). So "Download
as PDF" is a sibling of "New document" rather than a child of a
"Download" submenu. */}}
{{/* [docs-chrome] is the single sticky element wrapping both bars.
It was styled and never rendered for one review cycle, which cost
two things at once: [docs-bar] had been `position: sticky` before
this branch and stopped being, so the formatting bar scrolled away
— a regression against main sitting under a 30-line comment
describing the fix that replaced it; and File → Print printed the
menu bar and the toolbar across the top of page one, because the
print rule hides [docs-chrome] and nothing carried it.
Both rules were correct. Neither had an element. */}}
{{/* The formatting bar (fixtures/toolbar.mjs). Rendered server-side,
with every label resolved from the catalog (render.go's
docs.editor.toolbar.*) — the bundle carries no copy of its own,
the same rule [estilo-save-status] below already follows.
It is rendered even for a reader with no JavaScript, where it
does nothing at all. That is a deliberate choice and the weaker
of the two: the alternative is rendering it only when the editor
mounts, which means the page reflows under a person the moment
the bundle runs, and means the server template has to know
something it cannot know. A visible-but-inert bar is the smaller
harm, and the controls are all disabled until the toolbar's own
plugin enables them, so nothing here invites a click that would
silently do nothing.
role="toolbar" with an accessible name, and each control's name
from the catalog rather than from its glyph: a button whose only
content is a "B" announces as "B" to a screen reader without
one. */}}
{{/* The three selects opt into rastrillo's searchable-select
enhancement (ui/select.js, already served by render.go's
vendoredStaticFS and linked by the shell's own layout). Paul's
brief: "selects should be Rastrillo design system searchable
selects".
data-rst-select is the opt-in for a HAND-WRITTEN select —
ui/partials/field-select.html emits it only past ten options, on
the framework's judgement that search over a handful of items is
furniture rather than help. All three take it here anyway. Two
reasons: they sit side by side in one group, and one combobox
flanked by two native selects looks worse than either extreme;
and all three are growth surfaces — Size has eleven options
already, Style grows with named styles, and the face list is
built to grow. If the filter over three fonts reads as silly in
the morning it is one attribute to take off, which is why the
tension is recorded rather than resolved silently.
The three data-rst-select-* strings are the framework's own
catalog keys, resolved server-side (export.go) because select.js
builds its markup in the browser where the catalog is out of
reach.
Still `disabled` until the editor mounts, like every other
control in this bar — and select.js does NOT mirror that, so
toolbar.mjs mirrors it onto the combobox by hand. Left alone,
the enhancement ships a control that looks usable, accepts focus
and typing, and drives a disabled select behind it. */}}
{{/* Alignment: the first block-level formatting, so it gets a group
of its own between the inline marks and the list controls, which
is also where the block it acts on sits in the reading order.
aria-pressed rather than a select, and exactly one of the three
is pressed at any moment — toolbar.mjs's sync sets it from the
block the cursor is in, the same way Bold reads the marks there.
[docs-tool]'s 28px box already clears WCAG 2.2 AA's 24x24
minimum. These three are icon-only, so the pressed state is shown
by the inset bar under the glyph rather than by the weight change
that serves B/I/<> — see that rule's own comment, which used to
claim the weight covered every button and did not (§10.3: colour
never carries meaning alone). */}}
{{/* closes [docs-chrome] */}}
{{/* data-view is the person's paged/infinite preference, read from the
docs-view cookie server-side (menu.go's viewMode) so the FIRST
paint is already right. Doing it in the bundle instead would mean
anyone who prefers paged watches an infinite document reflow into
pages a moment after it appears.
data-page-label is the page-number template
("Page {n}") for fixtures/pages.mjs to stamp into each gap. Copy,
so it comes from the catalog here rather than being a literal in
the bundle — the rule the toolbar and the save-status indicator
already follow. */}}
{{/* docs-editor is the ProseMirror mount point (fixtures/editor-entry.js,
Task 6): the same [docs-text] container the scriptless read view
already renders into, not a second element beside it. ProseMirror
replaces this container's contents only once it mounts, so the
server-rendered {{.HTML}} below is what a scriptless reader sees
and what a scripted reader sees until the editor takes over — spec
§6.2, and editor_view_test.go's own
TestShowRendersContentWithoutScript pins it. data-sqid is the
bundle's only way to learn which document's update log
(/d/{{.Sqid}}/updates) to load and post to. */}}
{{/* A reader gets the same container without the mount point, the
data-sqid or (below) the script — absent, not disabled, because a
disabled editor is one CSS rule from being an editor again. The
server-rendered HTML is identical either way, so this is the
scriptless read view the whole page already falls back to. */}}
{{if .CanWrite}}