// Shared UI atoms for the edition — ink icons only.
// The raw ♥ codepoint renders as a broken/arrow-ish glyph on some Android fonts,
// so the heart, cross and arrow are inline SVG paths filled with currentColor.
//
// Reading-pass atoms (2026-08-31): ReadStamp, ReadAtButton and Headline live here
// because the front page, the section pages and the reader pane all draw them and
// they must not drift apart.
export function HeartIcon({ size = 11 }) {
return (
);
}
export function CrossIcon({ size = 9 }) {
return (
);
}
// North-east arrow — the "leaves the paper" glyph. Also SVG for the same font reason.
export function ArrowIcon({ size = 10 }) {
return (
);
}
const imgSrc = (u) => (u ? `/api/img?u=${encodeURIComponent(u)}` : null);
// Photo sized by its context (.ft-sec img, .ft-brief-item img, .ft-story img).
// No image -> ink hatching placeholder of the same box.
export function Thumb({ story, eager = false }) {
if (!story.image) return ;
return (
{ e.currentTarget.style.display = "none"; }}
/>
);
}
// Proof the story was read, drawn as a rubber stamp rather than a badge: rotated,
// letter-spaced, double-ruled. `opened` gets a dot — weaker evidence, weaker mark.
export function ReadStamp({ state }) {
if (!state) return null;
if (state.read) {
return (
READ
);
}
if (state.opened) return ;
return null;
}
// "Read at " — the only way out of the paper, so it is a button, not a
// whispered underlined link (Daniel, 2026-08-31). Outlined red + arrow; the one
// control allowed to open a new tab.
export function ReadAtButton({ story, size = "sm", as = "div", label }) {
const Tag = as === "span" ? "span" : "div";
return (
Read at {story.source || "source"}
{label && {label}}
);
}
// Headline as a real link that stays inside the paper: the pane, not a new tab.
// The browser never follows it (we have no ) — href keeps middle-click,
// copy-link-address and right-click alive for people who want the URL.
export function Headline({ story, onOpen, as = "h3", className, style, children }) {
const Tag = as;
return (
{
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button !== 0 || e.altKey) return;
e.preventDefault();
onOpen(story);
}}
>
{children || story.title}
);
}
export { imgSrc };