This document describes how a host asks which page it is dealing with: the page the user has selected, the page the work is on, and the page the pointer is over — and how a host shows a page itself.
It exists because get_active_page() returns a spread, and a spread can hold two pages. A
host attaching something to "the page the user picked" needs an answer one level finer than that.
These are canvas methods on window.typograph, so they work in the viewer bundle as well as
the editor.
For page structure changes — add, delete, duplicate, reorder — see
PageEvents.md, which also documents the hover event described below.
Source of truth:
get_selected_page()/get_focused_page()/show_spread()/show_page()in the canvas package atsrc/canvas.ts; the event name lives insrc/typograph_events.ts.
Every call here answers with the same pair:
{ container_index: 3, record_index: 1 }
container_index — which spread, indexing document.pages (what get_active_page()
returns one of).record_index — which page within that spread: 0 for a single page, 0 or 1 for a
facing pair.This is the same addressing logical_pages(), move_logical_page() and delete_logical_page()
use, so a pair from here can be handed straight to them:
const doc = typograph.get_typograph_document();
const flat = doc.logical_pages(); // [{container_index, record_index}, ...]
const sel = typograph.get_selected_page();
// the flat index the move/delete calls take
const index = flat.findIndex(p => p.container_index === sel.container_index
&& p.record_index === sel.record_index);
typograph.get_selected_page()What the user picked in the page bar — the blue selection.
const sel = typograph.get_selected_page();
Three possible answers, and all three mean something different:
| Returns | Meaning |
|---|---|
{container_index, record_index} |
one page of a spread is selected |
{container_index, record_index: null} |
the whole spread is selected |
null |
nothing is selected — or this embed has no page bar |
⚠️
record_index: nullis not "cannot tell". It says the user selected a spread rather than a page, and that is what the editor's own delete and duplicate act on: a selected page of a spread removes that one page, a selected spread removes the whole container. Treatingnullas page0will disagree with what the editor does next.
A whole-spread selection is reached by shift-clicking the second half of a spread, and by an
island or single-page row. null comes from clicking the page number badge, which activates a
spread without selecting pages.
The returned object is a copy — writing to it does not move the selection.
typograph.get_focused_page()Which page the work is on.
const page = typograph.get_focused_page(); // { container_index: 3, record_index: 0 }
The active spread, plus the page the current element selection sits on. When nothing is selected it keeps its last answer, and a click on an empty page area sets it.
Unlike get_selected_page() this always answers while a document is open, including in a
viewer where there is no page bar to click. That makes it the one to use for "apply this to the
current page".
get_selected_page() |
get_focused_page() |
|
|---|---|---|
| Answers | what the user picked in the page bar | which page the work is on |
| In a viewer (no page bar) | null |
answers |
| Can say "a whole spread" | yes, record_index: null |
no, always a page |
| Can say "nothing" | yes, null |
only when no document is open |
Use the first to follow the user's explicit choice, the second when you need a page regardless.
typograph.show_spread() and typograph.show_page() bring a spread, or one page of it, into
view — what a click on its row in the page bar does.
typograph.show_spread(2); // spread 2, selected whole
typograph.show_page({ container_index: 3, record_index: 1 }); // the second page of spread 3
show_page() takes the same pair get_selected_page() returns, so a page a host stored can be
shown again as it is; record_index: null is the whole spread. The page also becomes the focused
one, so get_focused_page() answers it.
What follows is exactly what a click does: the canvas shows the spread, the page bar's selection
moves to it, and typograph_page_shown (Page Action Events) fires — once, and
not at all when that spread was already the one shown. It works with the page bar hidden too, so a
viewer without one can drive its own navigation from these calls.
It is navigation, not an edit: nothing to undo, and is_changed() does not move.
Both return true when the page is shown, and false — changing nothing — when:
record_index past the spread's last page);set_visible_page_indexes() and this spread is not in it.Call it from typograph_document_opened: by then the document is fully open, its first spread is
shown and the page bar lists its pages.
document.addEventListener('typograph_document_opened', () => {
typograph.show_page({ container_index: 3, record_index: 1 });
});
To go by page number, logical_pages() lists every page in order, so page n is entry n − 1:
const page = typograph.get_typograph_document().logical_pages()[n - 1];
if (page) typograph.show_page(page);
Before release 20261008 the event fired before the canvas went back to the first spread, so a page
shown in the handler was replaced straight away; a setTimeout(…, 0) around the call worked around
it and can now go.
typograph.set_page_fill() gives one page a background colour of its own, or a whole spread one.
The colour is a swatch uuid from the document's swatch library, with an optional tint from 0 to 1.
typograph.set_page_fill({ container_index: 2, record_index: 1 }, swatch_uuid); // that page
typograph.set_page_fill({ container_index: 2, record_index: 1 }, swatch_uuid, 0.4); // at 40%
typograph.set_page_fill({ container_index: 2, record_index: 1 }, null); // back to the spread's colour
typograph.set_page_fill({ container_index: 2, record_index: null }, swatch_uuid); // the whole spread
typograph.get_page_fill({ container_index: 2, record_index: 1 });
// → { swatch_uuid: 'Cyan', tint: 1, own: true } (own: false when it is the spread's colour)
The colour belongs to the page: it stays with it when pages are deleted, moved or re-paired, and it is saved and printed on that page only — up to the spine, with the bleed on the outer edges. Colouring a whole spread replaces the colours its pages had of their own.
Each call is one undo step. It returns false, changing nothing, when the page or the swatch does
not exist, the tint is outside 0–1, a single page is given a gradient (a page's own colour is a
solid swatch; a gradient can colour a whole spread), or a spread is given null.
⚠️ Colouring a page through its spread object — get_active_page().set_fill_swatch_uuid(uuid) —
sets the spread's colour only. It does not record an undo step or redraw, and a page that has a
colour of its own keeps showing that. Use set_page_fill() instead.
typograph_page_hoverFired when the page under the pointer changes, on the rendered spread in the canvas.
document.addEventListener('typograph_page_hover', (e) => {
const { container_index, record_index } = e.detail;
if (record_index === null) clearLayoutPicker();
else showLayoutPickerFor(container_index, record_index);
});
| Property | Type | Description |
|---|---|---|
container_index |
number | null |
the spread under the pointer, null when over no page |
record_index |
number | null |
the page within it, null when over no page |
Both are null together. That is a real state, not a failure: the pointer can be over the canvas
without being over paper — the grey around the spread, or the gutter between two pages.
It fires once per boundary crossing, not continuously while the pointer stays on one page —
the same mechanism as
typograph_pageelement_hover, and like that
event it is not throttled, because each event is a meaningful transition.
Hover dispatch is off by default when the document was set up with interactive:false, and is
otherwise independent of it:
typograph.set_hover_events(true); // both hover events on
typograph.get_hover_events(); // → true
That independence is deliberate, so a viewer host can drive a layout picker or a page
highlight without making the document editable. Switching hover events off fires a final null
for both the element and the page hover, so a highlight is never left stuck.
⚠️ Element hover and page hover are independent. A page can have no element on it, and an element can overhang the paper onto the grey — so do not infer one from the other.
⚠️ Today the canvas draws one spread at a time, so
container_indexis the spread being viewed and the new information is which page of it. Written as a pair anyway, so a host does not have to correlate two sources.
function targetPage() {
// The explicit choice first; fall back to the page being worked on — a viewer has no page bar,
// and a user who selected a whole spread has not told us which half.
const sel = typograph.get_selected_page();
if (sel && sel.record_index !== null) return sel;
return typograph.get_focused_page();
}
document.getElementById('apply-layout').addEventListener('click', () => {
const page = targetPage();
if (!page) return; // no document open
applyLayout(page.container_index, page.record_index);
});
document.addEventListener('typograph_setup_ready', () => {
typograph.set_hover_events(true); // a view-only embed starts with them off
});
document.addEventListener('typograph_page_hover', (e) => {
const { container_index, record_index } = e.detail;
document.querySelectorAll('.page-chip.hovered')
.forEach(el => el.classList.remove('hovered'));
if (record_index === null) return;
document.querySelector(`.page-chip[data-spread="${container_index}"][data-page="${record_index}"]`)
?.classList.add('hovered');
});
| Call / event | Answers | Can be null |
|---|---|---|
typograph.get_selected_page() |
the page bar's selection | yes — nothing selected, or no page bar; record_index alone is null for a whole spread |
typograph.get_focused_page() |
the page the work is on | only when no document is open |
typograph_page_hover |
the page under the pointer, on change | both fields, when over no page |
typograph.set_hover_events(on) |
turns both hover events on or off | — |
typograph.show_spread(container_index) |
shows that spread, selected whole | — returns false when it cannot be shown |
typograph.show_page({container_index, record_index}) |
shows and selects that page | — returns false when it cannot be shown |
typograph.set_page_fill({container_index, record_index}, swatch_uuid, tint?) |
colours a page, or a whole spread with record_index: null |
— returns false when it cannot |
typograph.get_page_fill({container_index, record_index}) |
{swatch_uuid, tint, own} |
yes — no such page |