import type { BoardCard } from "./store"; const escapeHtml = (value: unknown) => String(value ?? "") .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """); export function renderLogin(message = "") { return pageShell( "Login", `

Private board

Admin

${message ? `

${escapeHtml(message)}

` : ""}
` ); } export function renderAdmin(cards: BoardCard[], options: { storageMode: string; message?: string }) { const sorted = [...cards].sort((a, b) => Date.parse(b.updatedAt) - Date.parse(a.updatedAt)); return pageShell( "Admin", `

Elysia admin

Доска
позора

${ options.message ? `
${escapeHtml(options.message)}
` : "" }

New card

${ sorted.length === 0 ? `
No cards yet.
` : sorted.map(renderCardEditor).join("") }
` ); } function renderCardEditor(card: BoardCard) { return `
${card.imageUrl ? `${escapeHtml(card.title)}` : `
No image
`}
`; } function pageShell(title: string, body: string) { return ` ${escapeHtml(title)} · Pozor Admin ${body} `; } const adminCss = ` /* Hallmark · pre-emit critique: P5 H4 E4 S5 R5 V4 */ :root { --color-black: #000000; --color-ink: #f7f7f7; --color-panel: #111111; --color-panel-raised: #181818; --color-muted: #a7a7a7; --color-line: #f7f7f7; --color-line-soft: #565656; --color-danger: #ffffff; --shadow-hard: 8px 8px 0 var(--color-line); --font-display: Georgia, "Times New Roman", serif; --font-body: Georgia, "Times New Roman", serif; --font-mono: "Courier New", monospace; } * { box-sizing: border-box; } html, body { min-height: 100%; margin: 0; overflow-x: clip; background: var(--color-black); color: var(--color-ink); color-scheme: dark; font-family: var(--font-body); } button, input, textarea { font: inherit; } button, input, textarea { border-radius: 0; } .adminShell, .loginShell { width: min(1480px, calc(100% - 32px)); margin: 0 auto; padding: 28px 0 64px; } .loginShell { display: grid; min-height: 100vh; place-items: center; } .loginPanel, .mast, .editor, .cardEditor, .empty, .noticeBanner { border: 2px solid var(--color-line); background: var(--color-panel); box-shadow: var(--shadow-hard); } .loginPanel { width: min(560px, 100%); padding: 28px; } .loginPanel h1 { font-size: clamp(3.6rem, 11vw, 7rem); line-height: 0.9; } .mast { display: grid; grid-template-columns: minmax(0, 1fr) minmax(240px, 360px); min-height: 290px; } .mast > div { padding: 28px; } .stamp { display: inline-block; margin: 0; padding: 7px 10px; background: var(--color-ink); color: var(--color-black); font-family: var(--font-mono); font-size: 0.78rem; font-weight: 900; text-transform: uppercase; } h1 { margin: 28px 0 0; font-family: var(--font-display); font-size: clamp(4rem, 10vw, 9rem); line-height: 0.78; overflow-wrap: anywhere; text-transform: uppercase; } .statusRail { display: grid; grid-template-rows: auto 1fr auto auto auto; gap: 18px; padding: 28px; border-left: 2px solid var(--color-line); background: var(--color-black); font-family: var(--font-mono); text-transform: uppercase; } .statusRail span { width: max-content; max-width: 100%; padding: 5px 7px; background: var(--color-ink); color: var(--color-black); font-weight: 900; } .statusRail strong { align-self: center; overflow-wrap: anywhere; } .workbench { display: grid; grid-template-columns: minmax(280px, 380px) minmax(0, 1fr); gap: 28px; align-items: start; padding-top: 36px; } .editor, .cardEditor, .empty, .noticeBanner { padding: 18px; } .noticeBanner { margin-top: 28px; color: var(--color-ink); font-family: var(--font-mono); overflow-wrap: anywhere; } .editor { position: sticky; top: 18px; } .cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr)); gap: 28px; } label { display: grid; gap: 8px; margin-top: 16px; } label span { color: var(--color-muted); font-family: var(--font-mono); font-size: 0.8rem; font-weight: 900; text-transform: uppercase; } input, textarea { width: 100%; min-width: 0; border: 2px solid var(--color-line); background: var(--color-black); color: var(--color-ink); padding: 10px 12px; } textarea { resize: vertical; } input:focus-visible, textarea:focus-visible, button:focus-visible { outline: 2px solid var(--color-line); outline-offset: 3px; } .checkline { grid-template-columns: auto minmax(0, 1fr); align-items: center; } .checkline input { width: 18px; height: 18px; } button { width: 100%; min-height: 44px; margin-top: 16px; border: 2px solid var(--color-line); background: var(--color-ink); color: var(--color-black); cursor: pointer; font-family: var(--font-mono); font-size: 0.86rem; font-weight: 900; text-transform: uppercase; transition: background 140ms ease, color 140ms ease, box-shadow 140ms ease; } .linkButton { display: grid; place-items: center; width: 100%; min-height: 44px; border: 2px solid var(--color-line); background: var(--color-ink); color: var(--color-black); font-family: var(--font-mono); font-size: 0.86rem; font-weight: 900; text-decoration: none; text-transform: uppercase; transition: background 140ms ease, color 140ms ease, box-shadow 140ms ease; } button:hover { background: var(--color-black); color: var(--color-ink); box-shadow: 4px 4px 0 var(--color-line); } .linkButton:hover, .linkButton:focus-visible { background: var(--color-black); color: var(--color-ink); outline: 2px solid var(--color-line); outline-offset: 3px; box-shadow: 4px 4px 0 var(--color-line); } .smallButton { margin-top: 0; } .danger { background: var(--color-black); color: var(--color-ink); } .rowActions { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; } .cardEditor img, .imageEmpty { display: block; width: 100%; aspect-ratio: 4 / 3; border: 2px solid var(--color-line); background: var(--color-black); object-fit: cover; } .imageEmpty, .empty, .notice { display: grid; place-items: center; min-height: 180px; color: var(--color-muted); } .notice { min-height: auto; place-items: start; margin: 24px 0 0; } .loginForm { margin-top: 28px; } @media (max-width: 860px) { .adminShell, .loginShell { width: min(100% - 20px, 720px); padding-top: 14px; } .mast, .workbench { grid-template-columns: minmax(0, 1fr); } .statusRail { border-top: 2px solid var(--color-line); border-left: 0; } .editor { position: static; } } @media (max-width: 420px) { .adminShell, .loginShell { width: min(100% - 14px, 390px); } .rowActions { grid-template-columns: minmax(0, 1fr); } } `;