/** * The page, rendered on the server: the first paint is complete HTML, and * `static/live.js` keeps the notes fresh afterwards. When a second page * arrives, move `layout` to `src/pages/layout.ts` and give each page a file. */ import { canDelete, type Note } from "./actions.ts"; import { commit, description, env, publicOrigin, ssh, title, where, } from "./env.ts"; import { asset, escape } from "./http.ts"; import { token, tokensHref } from "./tokens.ts"; import type { Who } from "./who.ts"; /** * The frame every page shares: the tokens and the look, who you are, and * what a link to the page says when it is shared — a title, a sentence * and the address, for Open Graph and the card a chat app unfurls. */ export function layout(body: string, who: Who, path = "/"): string { const door = who.role === "anon" ? `Sign in` : `${escape(who.name)}${who.role === "admin" ? " · admin" : ""} Sign out`; return ` ${escape(title)}

${escape(title)}

${escape(description)}

${body}
`; } export function home(input: { who: Who; notes: readonly Note[]; notice?: string | undefined; }): string { const { who, notes, notice } = input; const composer = who.role === "anon" ? `

Reading as a guest. Sign in to add a note.

` : `
`; const list = notes.length === 0 ? `

No notes yet. Notes written here stay in ${escape(where)} data.

` : ``; return layout( `
${composer}
${list}
${notice === undefined ? "" : `

${escape(notice)}

`}`, who, ); } const item = (note: Note, who: Who): string => `
  • ${escape(note.body)}

    ${escape(note.author_name)} ·
    ${ canDelete(who, note) ? `
    ` : "" }
  • `; const ago = (iso: string): string => { const s = Math.max(0, (Date.now() - Date.parse(iso)) / 1000); if (s < 60) return "just now"; if (s < 3600) return `${Math.floor(s / 60)}m ago`; if (s < 86400) return `${Math.floor(s / 3600)}h ago`; return new Date(iso).toLocaleDateString("en", { month: "short", day: "numeric", }); };