/** * Served-media rendering primitives: the discipline for showing platform media by * reference. A feature renders media through these so the reference gate, the origin * join, and the per-image load-failure handling live here ONCE and are never * reimplemented per feature. A raw-storing sink opts in to inline `data:image/*` via * `allowDataImage`; every other caller renders only served references and `https:` urls. * * UNTRUSTED PAYLOADS: a `url` arrives from an untrusted sender. It reaches an * attribute sink ONLY through {@link isRenderableMediaSrc} (an `https:` URL, the * platform's own served-media reference, or — for a raw-storing sink that opts in — * an inline `data:image/*` URI) joined by {@link resolveMediaSrc}; any other-shaped * url is a loud blocked notice, its text React-escaped, never a live attribute. * There is no `dangerouslySetInnerHTML` here. */ import type { ServedConstants } from '@tai42/api-client'; import type { CSSProperties, ReactNode } from 'react'; import { useState } from 'react'; import { useApi } from '../hooks/useApi'; import { Badge } from './badge'; import { isSafeHttpUrl } from './primitives'; /** * The served-media grammar the contract publishes, read from the injected client * (`useApi().constants`): the served route prefix and the stored-media id pattern. Media is * stored by reference and served from the API origin at `MEDIA_ROUTE_PREFIX + `; a record * carries it as a RELATIVE url of exactly that shape, resolved to the API base at render time * ({@link resolveMediaSrc}), not assumed same-origin as the SPA page. */ export type ServedMediaGrammar = { readonly [K in keyof Pick]: string; }; /** * Whether `url` is the platform's own served-media reference: a relative * `MEDIA_ROUTE_PREFIX + ` whose id matches `MEDIA_ID_PATTERN` whole. A served * reference is same-origin to the API base ({@link resolveMediaSrc}) and leaks no page * context on load, so a sink with no per-element referrer control (`