@aparte/plugin-cards 0.0.0-stage → 0.29.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +151 -2
- package/dist/carousel-DwgHvriV.js +63 -0
- package/dist/carousel-DwgHvriV.js.map +1 -0
- package/dist/controls-BYUm4aRV.js +62 -0
- package/dist/controls-BYUm4aRV.js.map +1 -0
- package/dist/format-BFq79ydr.js +728 -0
- package/dist/format-BFq79ydr.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +120 -0
- package/dist/index.js.map +1 -0
- package/dist/items/icons.d.ts +12 -0
- package/dist/items/icons.d.ts.map +1 -0
- package/dist/items/index.d.ts +100 -0
- package/dist/items/index.d.ts.map +1 -0
- package/dist/items.js +698 -0
- package/dist/items.js.map +1 -0
- package/dist/kit/carousel.d.ts +17 -0
- package/dist/kit/carousel.d.ts.map +1 -0
- package/dist/kit/controls.d.ts +51 -0
- package/dist/kit/controls.d.ts.map +1 -0
- package/dist/kit/define.d.ts +201 -0
- package/dist/kit/define.d.ts.map +1 -0
- package/dist/kit/dom.d.ts +40 -0
- package/dist/kit/dom.d.ts.map +1 -0
- package/dist/kit/fence.d.ts +39 -0
- package/dist/kit/fence.d.ts.map +1 -0
- package/dist/kit/format.d.ts +49 -0
- package/dist/kit/format.d.ts.map +1 -0
- package/dist/kit/rating.d.ts +19 -0
- package/dist/kit/rating.d.ts.map +1 -0
- package/dist/kit/sparkline.d.ts +19 -0
- package/dist/kit/sparkline.d.ts.map +1 -0
- package/dist/kit/strings.d.ts +139 -0
- package/dist/kit/strings.d.ts.map +1 -0
- package/dist/link/icons.d.ts +16 -0
- package/dist/link/icons.d.ts.map +1 -0
- package/dist/link/index.d.ts +98 -0
- package/dist/link/index.d.ts.map +1 -0
- package/dist/link.js +387 -0
- package/dist/link.js.map +1 -0
- package/dist/media/icons.d.ts +28 -0
- package/dist/media/icons.d.ts.map +1 -0
- package/dist/media/index.d.ts +107 -0
- package/dist/media/index.d.ts.map +1 -0
- package/dist/media.js +610 -0
- package/dist/media.js.map +1 -0
- package/dist/order/icons.d.ts +49 -0
- package/dist/order/icons.d.ts.map +1 -0
- package/dist/order/index.d.ts +204 -0
- package/dist/order/index.d.ts.map +1 -0
- package/dist/order.js +1843 -0
- package/dist/order.js.map +1 -0
- package/dist/place/icons.d.ts +18 -0
- package/dist/place/icons.d.ts.map +1 -0
- package/dist/place/index.d.ts +207 -0
- package/dist/place/index.d.ts.map +1 -0
- package/dist/place.js +1456 -0
- package/dist/place.js.map +1 -0
- package/dist/plan/icons.d.ts +25 -0
- package/dist/plan/icons.d.ts.map +1 -0
- package/dist/plan/index.d.ts +153 -0
- package/dist/plan/index.d.ts.map +1 -0
- package/dist/plan.js +1191 -0
- package/dist/plan.js.map +1 -0
- package/dist/product/icons.d.ts +16 -0
- package/dist/product/icons.d.ts.map +1 -0
- package/dist/product/index.d.ts +133 -0
- package/dist/product/index.d.ts.map +1 -0
- package/dist/product.js +1245 -0
- package/dist/product.js.map +1 -0
- package/dist/rating-CHVajQ2b.js +35 -0
- package/dist/rating-CHVajQ2b.js.map +1 -0
- package/dist/score/icons.d.ts +35 -0
- package/dist/score/icons.d.ts.map +1 -0
- package/dist/score/index.d.ts +185 -0
- package/dist/score/index.d.ts.map +1 -0
- package/dist/score.js +1843 -0
- package/dist/score.js.map +1 -0
- package/dist/sparkline-C-cA9obz.js +56 -0
- package/dist/sparkline-C-cA9obz.js.map +1 -0
- package/dist/stats/icons.d.ts +19 -0
- package/dist/stats/icons.d.ts.map +1 -0
- package/dist/stats/index.d.ts +137 -0
- package/dist/stats/index.d.ts.map +1 -0
- package/dist/stats.js +1085 -0
- package/dist/stats.js.map +1 -0
- package/dist/stock/chart.d.ts +135 -0
- package/dist/stock/chart.d.ts.map +1 -0
- package/dist/stock/icons.d.ts +15 -0
- package/dist/stock/icons.d.ts.map +1 -0
- package/dist/stock/index.d.ts +195 -0
- package/dist/stock/index.d.ts.map +1 -0
- package/dist/stock.js +1013 -0
- package/dist/stock.js.map +1 -0
- package/dist/weather/conditions.d.ts +25 -0
- package/dist/weather/conditions.d.ts.map +1 -0
- package/dist/weather/icons.d.ts +12 -0
- package/dist/weather/icons.d.ts.map +1 -0
- package/dist/weather/index.d.ts +146 -0
- package/dist/weather/index.d.ts.map +1 -0
- package/dist/weather.js +1162 -0
- package/dist/weather.js.map +1 -0
- package/package.json +137 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.js","sources":["../src/media/icons.ts","../src/media/media.css?raw","../src/media/index.ts"],"sourcesContent":["/**\n * The media card's glyphs: one line drawing per kind of medium (for the placeholder a\n * tile shows before, or instead of, its picture), the play triangle a video wears, the\n * stack of frames a gallery is named with, and the small marks of a caption line.\n *\n * Drawn on a 24 × 24 grid in `currentColor`, so the sheet colours them through `color`\n * and a theme follows. Constants of this package — the only markup the card parses.\n */\nimport type { MediaKind } from './index.js';\n\nconst svg = (body: string) =>\n `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" xmlns=\"http://www.w3.org/2000/svg\">${body}</svg>`; // safe-text: this package's own SVG, built from its glyph constants\n\n/** A framed landscape: an image. */\nexport const IMAGE = svg('<rect x=\"3.5\" y=\"4.5\" width=\"17\" height=\"15\" rx=\"2.5\"/><circle cx=\"9\" cy=\"9.5\" r=\"1.6\"/><path d=\"m4 17.5 4.6-4.6a1.4 1.4 0 0 1 2 0l3.4 3.4 1.6-1.6a1.4 1.4 0 0 1 2 0l2.9 2.9\"/>');\n\n/** A strip of film: a video. */\nexport const FILM = svg('<rect x=\"3.5\" y=\"5\" width=\"17\" height=\"14\" rx=\"2.5\"/><path d=\"M7.5 5v14M16.5 5v14M3.5 9.5h4M3.5 14.5h4M16.5 9.5h4M16.5 14.5h4\"/>');\n\n/** A note: a sound. */\nexport const NOTE = svg('<path d=\"M9.5 17.5V6l10-2v11.5\"/><circle cx=\"7\" cy=\"17.5\" r=\"2.5\"/><circle cx=\"17\" cy=\"15.5\" r=\"2.5\"/>');\n\n/** Two frames, one behind the other: a gallery, and the card's one-line state glyph. */\nexport const FRAMES = svg('<rect x=\"7\" y=\"3.5\" width=\"13.5\" height=\"11.5\" rx=\"2.2\"/><path d=\"M17 18.5a2 2 0 0 1-2 2H5.5a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2\"/><path d=\"m7.5 13 3-3 2.5 2.5 1.5-1.5 3.5 3.5\"/>');\n\n/** The play triangle, filled: it sits on a disc over a picture. */\nexport const PLAY = '<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8.5 5.6v12.8a1 1 0 0 0 1.5.86l10.2-6.4a1 1 0 0 0 0-1.72L10 4.74a1 1 0 0 0-1.5.86z\"/></svg>';\n\n/** Four corners pulled out: \"see it larger\", in the corner of a picture you can open. */\nexport const EXPAND = svg('<path d=\"M14.5 4.5h5v5M9.5 19.5h-5v-5M19.5 4.5l-6 6M4.5 19.5l6-6\"/>');\n\n/** A box with an arrow leaving it: a page that opens elsewhere. */\nexport const EXTERNAL = svg('<path d=\"M13.5 4.5h6v6M19.5 4.5l-8 8\"/><path d=\"M17.5 13.5v4a2 2 0 0 1-2 2h-9a2 2 0 0 1-2-2v-9a2 2 0 0 1 2-2h4\"/>');\n\n/** A camera body: who took or made the picture. */\nexport const CAMERA = svg('<path d=\"M4.5 8h3l1.6-2.3h5.8L16.5 8h3a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1h-15a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1z\"/><circle cx=\"12\" cy=\"13\" r=\"3.3\"/>');\n\n/** The glyph for a kind of medium. */\nexport function kindGlyph(kind: MediaKind): string {\n return kind === 'video' ? FILM : kind === 'audio' ? NOTE : IMAGE;\n}\n","export default \"/* =========================================\\n The media card — @aparte/plugin-cards/media\\n =========================================\\n Three variants of one card, chosen by `data-variant` on its root:\\n single one medium, large: an image at its own shape within a height (a button that\\n opens the lightbox), a video in the browser's player, a sound under a header\\n with its cover and a waveform, a YouTube page as a poster that links out.\\n One medium is drawn this way whatever the variant.\\n gallery a mosaic: two to six tiles, the first larger from three, \\\"+n\\\" on the last\\n when there are more. Several media in `single` are drawn this way too.\\n strip square thumbnails that scroll sideways.\\n Every tile opens the lightbox: a native <dialog> wearing core's `.aparte-dialog`, its\\n picture on a dark stage.\\n Every colour is a core token or a `--aparte-media-*` one here. The stage is black in\\n either theme — what a photo is looked at on — and so is the scrim over a picture: they\\n sit on images, not on the page's ground.\\n */\\n\\n.aparte-media-card {\\n /* The tallest a picture is drawn on the card. */\\n --aparte-media-max-block: 22rem;\\n /* The seams of the mosaic. */\\n --aparte-media-gap: 2px;\\n /* The tint of a placeholder, a cover without a picture, the waveform. */\\n --aparte-media-tint: var(--aparte-primary);\\n --aparte-media-stage: #0b0b0d;\\n --aparte-media-on-stage: #ffffff;\\n --aparte-media-scrim: rgb(0 0 0 / 0.55);\\n --aparte-media-lightbox-block: min(72dvh, 44rem);\\n}\\n\\n.aparte-media-card:has(> .aparte-media--single) { --aparte-rich-card-max-inline-size: 24rem; }\\n.aparte-media-card:has(> .aparte-media--strip) { --aparte-rich-card-max-inline-size: 30rem; }\\n\\n.aparte-media {\\n min-inline-size: 0;\\n margin: 0;\\n color: var(--aparte-text);\\n}\\n\\n/* ── What stands behind a picture ────────────────────────────────────── */\\n\\n.aparte-media__placeholder {\\n position: absolute;\\n inset: 0;\\n z-index: 0;\\n display: grid;\\n place-items: center;\\n background:\\n radial-gradient(circle at 30% 20%, color-mix(in oklab, var(--aparte-media-tint) 14%, transparent), transparent 60%),\\n color-mix(in oklab, var(--aparte-media-tint) 8%, var(--aparte-surface-2));\\n color: color-mix(in oklab, var(--aparte-media-tint) 55%, var(--aparte-text-muted));\\n}\\n\\n.aparte-media__placeholder-glyph { --aparte-rich-card-glyph-size: 1.75rem; opacity: 0.85; }\\n\\n.aparte-media__img {\\n position: relative;\\n z-index: 1;\\n display: block;\\n}\\n\\n/* ── One image, at its own shape ─────────────────────────────────────── */\\n\\n.aparte-media--single {\\n display: flex;\\n flex-direction: column;\\n padding: var(--aparte-space-4) var(--aparte-space-4) var(--aparte-space-5);\\n}\\n\\n.aparte-media__frame {\\n position: relative;\\n display: block;\\n inline-size: 100%;\\n margin: 0;\\n padding: 0;\\n overflow: hidden;\\n isolation: isolate;\\n border: 0;\\n border-radius: var(--aparte-radius-lg);\\n background: var(--aparte-surface-2);\\n color: inherit;\\n font: inherit;\\n}\\n\\n/* Nothing known of its shape: the picture sets the frame's size, within the card and the height. */\\n.aparte-media__frame[data-kind=\\\"image\\\"] {\\n align-self: center;\\n inline-size: fit-content;\\n max-inline-size: 100%;\\n cursor: zoom-in;\\n}\\n\\n.aparte-media__frame[data-kind=\\\"image\\\"] > .aparte-media__img {\\n inline-size: auto;\\n block-size: auto;\\n max-inline-size: 100%;\\n max-block-size: var(--aparte-media-max-block);\\n}\\n\\n/* Its shape known: the frame is drawn at it from the start — as wide as the card allows,\\n narrower when the height would pass the limit — and the picture fills it. */\\n.aparte-media__frame[data-kind=\\\"image\\\"][data-sized] {\\n inline-size: min(100%, calc(var(--aparte-media-max-block) * var(--aparte-media-ratio-n)));\\n aspect-ratio: var(--aparte-media-ratio);\\n}\\n\\n.aparte-media__frame[data-kind=\\\"image\\\"][data-sized] > .aparte-media__img {\\n inline-size: 100%;\\n block-size: 100%;\\n max-block-size: none;\\n object-fit: cover;\\n}\\n\\n/* Loading with no shape known, or failed: a box of a reasonable size around the placeholder. */\\n.aparte-media__frame[data-kind=\\\"image\\\"]:not([data-sized]):is([data-loading], [data-broken]) {\\n inline-size: 100%;\\n min-block-size: 10rem;\\n}\\n\\n.aparte-media__frame[data-kind=\\\"image\\\"] > .aparte-media__img {\\n transition: transform var(--aparte-duration-slower, 0.3s) ease, filter var(--aparte-duration-slower, 0.3s) ease;\\n}\\n\\n.aparte-media__frame[data-kind=\\\"image\\\"]:hover > .aparte-media__img { transform: scale(1.025); }\\n\\n.aparte-media__frame:focus-visible {\\n outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);\\n outline-offset: var(--aparte-focus-outline-offset);\\n}\\n\\n/* \\\"See it larger\\\": a frosted disc in the corner, shown on hover and focus. */\\n.aparte-media__zoom {\\n --aparte-rich-card-glyph-size: 2rem;\\n position: absolute;\\n inset-block-start: var(--aparte-space-4);\\n inset-inline-end: var(--aparte-space-4);\\n z-index: 2;\\n padding: 0.4rem;\\n border-radius: var(--aparte-radius-full);\\n background: var(--aparte-media-scrim);\\n color: var(--aparte-media-on-stage);\\n backdrop-filter: blur(6px);\\n opacity: 0;\\n transition: opacity var(--aparte-duration-base, 0.15s) ease;\\n}\\n\\n.aparte-media__frame:hover > .aparte-media__zoom,\\n.aparte-media__frame:focus-visible > .aparte-media__zoom { opacity: 1; }\\n\\n.aparte-media__frame[data-broken] > .aparte-media__zoom { display: none; }\\n\\n/* ── A video ─────────────────────────────────────────────────────────── */\\n\\n.aparte-media__frame--video,\\n.aparte-media__external,\\n.aparte-media__lb-video {\\n aspect-ratio: var(--aparte-media-ratio, 16 / 9);\\n max-block-size: var(--aparte-media-max-block);\\n background: var(--aparte-media-stage);\\n}\\n\\n.aparte-media__video {\\n display: block;\\n inline-size: 100%;\\n block-size: 100%;\\n object-fit: contain;\\n background: var(--aparte-media-stage);\\n}\\n\\n/* A page that plays it elsewhere: its poster, a disc with the triangle, the site, the length. */\\n.aparte-media__external {\\n color: var(--aparte-media-on-stage);\\n text-decoration: none;\\n}\\n\\n.aparte-media__external > .aparte-media__img {\\n position: absolute;\\n inset: 0;\\n inline-size: 100%;\\n block-size: 100%;\\n object-fit: cover;\\n transition: transform var(--aparte-duration-slower, 0.3s) ease;\\n}\\n\\n.aparte-media__external:hover > .aparte-media__img { transform: scale(1.03); }\\n\\n.aparte-media__external:focus-visible {\\n outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);\\n outline-offset: var(--aparte-focus-outline-offset);\\n}\\n\\n.aparte-media__play {\\n position: absolute;\\n inset-block-start: 50%;\\n inset-inline-start: 50%;\\n z-index: 2;\\n display: grid;\\n place-items: center;\\n inline-size: 3.5rem;\\n block-size: 3.5rem;\\n border-radius: var(--aparte-radius-full);\\n background: var(--aparte-media-scrim);\\n color: var(--aparte-media-on-stage);\\n box-shadow: 0 0 0 1px rgb(255 255 255 / 0.25) inset, 0 6px 20px -6px rgb(0 0 0 / 0.6);\\n backdrop-filter: blur(6px);\\n translate: -50% -50%;\\n transition: background-color var(--aparte-duration-base, 0.15s) ease, scale var(--aparte-duration-base, 0.15s) ease;\\n}\\n\\n/* `translate` is physical: in a right-to-left page the start edge is the right one. */\\n.aparte-media__play:dir(rtl) { translate: 50% -50%; }\\n\\n.aparte-media__play-glyph { --aparte-rich-card-glyph-size: 1.5rem; margin-inline-start: 0.15rem; }\\n.aparte-media__play:dir(rtl) .aparte-media__play-glyph { margin-inline: 0 0.15rem; transform: scaleX(-1); }\\n\\n.aparte-media__play--sm { inline-size: 2.5rem; block-size: 2.5rem; }\\n.aparte-media__play--sm .aparte-media__play-glyph { --aparte-rich-card-glyph-size: 1.1rem; }\\n\\n.aparte-media__external:hover .aparte-media__play,\\n.aparte-media__tile:hover .aparte-media__play {\\n background: var(--aparte-primary);\\n scale: 1.08;\\n}\\n\\n.aparte-media__site,\\n.aparte-media__duration {\\n position: absolute;\\n inset-block-end: var(--aparte-space-4);\\n z-index: 2;\\n display: inline-flex;\\n align-items: center;\\n gap: var(--aparte-space-2);\\n padding: var(--aparte-space-1) var(--aparte-space-3);\\n border-radius: var(--aparte-radius-sm);\\n background: var(--aparte-media-scrim);\\n color: var(--aparte-media-on-stage);\\n font-size: var(--aparte-font-size-xs);\\n font-weight: var(--aparte-font-weight-semibold);\\n line-height: 1.3;\\n white-space: nowrap;\\n}\\n\\n.aparte-media__site { inset-inline-start: var(--aparte-space-4); }\\n.aparte-media__duration { inset-inline-end: var(--aparte-space-4); }\\n.aparte-media__site-glyph { --aparte-rich-card-glyph-size: 0.8rem; }\\n\\n/* ── A sound ─────────────────────────────────────────────────────────── */\\n\\n.aparte-media__audio {\\n display: grid;\\n gap: var(--aparte-space-4);\\n padding: var(--aparte-space-3) var(--aparte-space-2) 0;\\n}\\n\\n.aparte-media__audio-head {\\n display: flex;\\n align-items: center;\\n gap: var(--aparte-space-5);\\n min-inline-size: 0;\\n}\\n\\n.aparte-media__cover {\\n position: relative;\\n flex: none;\\n inline-size: 3.5rem;\\n block-size: 3.5rem;\\n overflow: hidden;\\n border-radius: var(--aparte-radius-lg);\\n box-shadow: 0 4px 14px -8px color-mix(in oklab, var(--aparte-media-tint) 70%, black);\\n}\\n\\n.aparte-media__cover > .aparte-media__img {\\n position: absolute;\\n inset: 0;\\n inline-size: 100%;\\n block-size: 100%;\\n object-fit: cover;\\n}\\n\\n.aparte-media__cover > .aparte-media__placeholder {\\n background: linear-gradient(135deg, color-mix(in oklab, var(--aparte-media-tint) 30%, var(--aparte-surface-1)), color-mix(in oklab, var(--aparte-media-tint) 10%, var(--aparte-surface-2)));\\n}\\n\\n.aparte-media__audio-text {\\n display: flex;\\n flex: 1;\\n flex-direction: column;\\n gap: var(--aparte-space-1);\\n min-inline-size: 0;\\n}\\n\\n.aparte-media__audio-title,\\n.aparte-media__audio-credit {\\n overflow: hidden;\\n text-overflow: ellipsis;\\n white-space: nowrap;\\n}\\n\\n.aparte-media__audio-title {\\n font-size: var(--aparte-font-size-base);\\n font-weight: var(--aparte-font-weight-semibold);\\n}\\n\\n.aparte-media__audio-credit,\\n.aparte-media__audio-length {\\n color: var(--aparte-text-muted);\\n font-size: var(--aparte-font-size-sm);\\n}\\n\\n.aparte-media__audio-length { flex: none; }\\n\\n/* A waveform that is only decoration: bars cut by a mask, their tops following a fixed\\n envelope cut by a polygon. It shows no real level, so it says nothing to a reader. */\\n.aparte-media__wave {\\n display: block;\\n block-size: 2.25rem;\\n background: linear-gradient(90deg,\\n var(--aparte-media-tint) 0%,\\n color-mix(in oklab, var(--aparte-media-tint) 70%, transparent) 50%,\\n color-mix(in oklab, var(--aparte-media-tint) 28%, transparent) 50%,\\n color-mix(in oklab, var(--aparte-media-tint) 28%, transparent) 100%);\\n background-size: 200% 100%;\\n background-position: 100% 0;\\n -webkit-mask-image: repeating-linear-gradient(90deg, #000 0 3px, transparent 3px 6px);\\n mask-image: repeating-linear-gradient(90deg, #000 0 3px, transparent 3px 6px);\\n clip-path: polygon(\\n 0% 35%, 5% 22.5%, 10% 30%, 15% 10%, 20% 20%, 25% 2.5%, 30% 15%, 35% 27.5%, 40% 7.5%, 45% 25%, 50% 12.5%,\\n 55% 32.5%, 60% 5%, 65% 20%, 70% 30%, 75% 15%, 80% 25%, 85% 10%, 90% 27.5%, 95% 35%, 100% 37.5%,\\n 100% 62.5%, 95% 65%, 90% 72.5%, 85% 90%, 80% 75%, 75% 85%, 70% 70%, 65% 80%, 60% 95%, 55% 67.5%,\\n 50% 87.5%, 45% 75%, 40% 92.5%, 35% 72.5%, 30% 85%, 25% 97.5%, 20% 80%, 15% 90%, 10% 70%, 5% 77.5%, 0% 65%);\\n}\\n\\n/* While it plays, the colour sweeps across the bars. */\\n.aparte-media__audio[data-playing] .aparte-media__wave {\\n animation: aparte-media-wave 2.4s linear infinite;\\n}\\n\\n@keyframes aparte-media-wave {\\n from { background-position: 100% 0; }\\n to { background-position: 0% 0; }\\n}\\n\\n.aparte-media__player {\\n display: block;\\n inline-size: 100%;\\n block-size: 2.5rem;\\n}\\n\\n/* ── Under a medium ──────────────────────────────────────────────────── */\\n\\n.aparte-media__figcaption {\\n display: flex;\\n flex-direction: column;\\n gap: var(--aparte-space-1);\\n min-inline-size: 0;\\n padding: var(--aparte-space-4) var(--aparte-space-2) 0;\\n}\\n\\n.aparte-media__title {\\n font-size: var(--aparte-font-size-base);\\n font-weight: var(--aparte-font-weight-semibold);\\n letter-spacing: -0.005em;\\n line-height: 1.3;\\n overflow-wrap: anywhere;\\n}\\n\\n.aparte-media__caption {\\n display: -webkit-box;\\n margin: 0;\\n overflow: hidden;\\n color: var(--aparte-text-muted);\\n font-size: var(--aparte-font-size-md);\\n line-height: 1.45;\\n overflow-wrap: anywhere;\\n -webkit-box-orient: vertical;\\n -webkit-line-clamp: 4;\\n}\\n\\n.aparte-media__meta {\\n display: flex;\\n flex-wrap: wrap;\\n align-items: center;\\n gap: var(--aparte-space-2) var(--aparte-space-5);\\n margin-block-start: var(--aparte-space-2);\\n color: var(--aparte-text-muted);\\n font-size: var(--aparte-font-size-sm);\\n}\\n\\n.aparte-media__credit {\\n display: inline-flex;\\n align-items: center;\\n gap: var(--aparte-space-2);\\n min-inline-size: 0;\\n}\\n\\n.aparte-media__source {\\n display: inline-flex;\\n align-items: center;\\n gap: var(--aparte-space-1);\\n border-radius: var(--aparte-radius-xs);\\n color: var(--aparte-primary-ink, var(--aparte-text));\\n font-weight: var(--aparte-font-weight-medium);\\n text-decoration: none;\\n}\\n\\n.aparte-media__source:hover {\\n text-decoration: underline;\\n text-decoration-thickness: 1px;\\n text-underline-offset: 0.18em;\\n}\\n\\n.aparte-media__source:focus-visible {\\n outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);\\n outline-offset: var(--aparte-focus-outline-offset);\\n}\\n\\n.aparte-media__line-glyph { --aparte-rich-card-glyph-size: 0.95rem; }\\n.aparte-media__source .aparte-media__line-glyph { --aparte-rich-card-glyph-size: 0.8rem; }\\n\\n/* ── Tiles: the mosaic's and the strip's ─────────────────────────────── */\\n\\n.aparte-media__tile {\\n position: relative;\\n display: block;\\n inline-size: 100%;\\n block-size: 100%;\\n margin: 0;\\n padding: 0;\\n overflow: hidden;\\n border: 0;\\n background: var(--aparte-surface-2);\\n color: inherit;\\n font: inherit;\\n cursor: zoom-in;\\n}\\n\\n.aparte-media__tile > .aparte-media__img {\\n position: absolute;\\n inset: 0;\\n inline-size: 100%;\\n block-size: 100%;\\n object-fit: cover;\\n transition: transform var(--aparte-duration-slower, 0.3s) ease, filter var(--aparte-duration-slower, 0.3s) ease;\\n}\\n\\n.aparte-media__tile:hover > .aparte-media__img {\\n transform: scale(1.05);\\n filter: brightness(1.05);\\n}\\n\\n/* The focus ring is drawn inside the tile, over the picture: tiles touch each other and the\\n card's edge, so a ring outside would be cut. Two rings, so it reads on any picture. */\\n.aparte-media__tile::after {\\n content: \\\"\\\";\\n position: absolute;\\n inset: 0;\\n z-index: 4;\\n border-radius: inherit;\\n pointer-events: none;\\n}\\n\\n.aparte-media__tile:focus-visible { outline: none; }\\n\\n.aparte-media__tile:focus-visible::after {\\n box-shadow:\\n inset 0 0 0 var(--aparte-focus-outline-width) var(--aparte-border-focus),\\n inset 0 0 0 calc(var(--aparte-focus-outline-width) * 2) var(--aparte-surface-1);\\n}\\n\\n.aparte-media__tile[data-kind=\\\"audio\\\"] { cursor: pointer; }\\n\\n.aparte-media__tile-title {\\n position: absolute;\\n inset-inline: var(--aparte-space-3);\\n inset-block-end: var(--aparte-space-3);\\n z-index: 2;\\n overflow: hidden;\\n color: var(--aparte-text);\\n font-size: var(--aparte-font-size-xs);\\n font-weight: var(--aparte-font-weight-semibold);\\n text-overflow: ellipsis;\\n white-space: nowrap;\\n}\\n\\n.aparte-media__tile .aparte-media__duration {\\n inset-block-end: var(--aparte-space-3);\\n inset-inline-end: var(--aparte-space-3);\\n}\\n\\n/* \\\"+3\\\": how many more the lightbox holds, over the last tile. */\\n.aparte-media__more {\\n position: absolute;\\n inset: 0;\\n z-index: 3;\\n display: grid;\\n place-items: center;\\n background: var(--aparte-media-scrim);\\n color: var(--aparte-media-on-stage);\\n font-size: var(--aparte-font-size-2xl);\\n font-weight: var(--aparte-font-weight-semibold);\\n letter-spacing: -0.01em;\\n backdrop-filter: blur(1.5px);\\n transition: background-color var(--aparte-duration-base, 0.15s) ease;\\n}\\n\\n.aparte-media__tile:hover > .aparte-media__more { background: rgb(0 0 0 / 0.42); }\\n\\n/* ── Gallery: the mosaic ─────────────────────────────────────────────── */\\n\\n.aparte-media--gallery {\\n display: flex;\\n flex-direction: column;\\n}\\n\\n/* A six-column grid laid out per count, so every count fills its box with no hole:\\n 2 two squares\\n 3 a large square, two small ones stacked beside it\\n 4 a wide one over three\\n 5 two over three\\n 6 a large square, two beside it, three under it */\\n.aparte-media__grid {\\n display: grid;\\n grid-template-columns: repeat(6, minmax(0, 1fr));\\n gap: var(--aparte-media-gap);\\n margin: 0;\\n padding: 0;\\n list-style: none;\\n aspect-ratio: 3 / 2;\\n}\\n\\n.aparte-media__grid > * {\\n display: flex;\\n grid-column: span 2;\\n min-inline-size: 0;\\n min-block-size: 0;\\n}\\n\\n.aparte-media__grid > * > .aparte-media__tile { flex: 1; }\\n\\n.aparte-media__grid[data-count=\\\"2\\\"] { grid-template-rows: minmax(0, 1fr); aspect-ratio: 2 / 1; }\\n.aparte-media__grid[data-count=\\\"2\\\"] > * { grid-column: span 3; }\\n\\n.aparte-media__grid[data-count=\\\"3\\\"] { grid-template-rows: repeat(2, minmax(0, 1fr)); }\\n.aparte-media__grid[data-count=\\\"3\\\"] > :first-child { grid-column: span 4; grid-row: span 2; }\\n\\n.aparte-media__grid[data-count=\\\"4\\\"] { grid-template-rows: minmax(0, 2fr) minmax(0, 1fr); aspect-ratio: 4 / 3; }\\n.aparte-media__grid[data-count=\\\"4\\\"] > :first-child { grid-column: span 6; }\\n\\n.aparte-media__grid[data-count=\\\"5\\\"] { grid-template-rows: repeat(2, minmax(0, 1fr)); }\\n.aparte-media__grid[data-count=\\\"5\\\"] > :nth-child(-n + 2) { grid-column: span 3; }\\n\\n.aparte-media__grid[data-count=\\\"6\\\"] { grid-template-rows: repeat(3, minmax(0, 1fr)); aspect-ratio: 1; }\\n.aparte-media__grid[data-count=\\\"6\\\"] > :first-child { grid-column: span 4; grid-row: span 2; }\\n\\n/* The collection's line: under the mosaic, over the strip. */\\n.aparte-media__heading {\\n display: flex;\\n align-items: center;\\n gap: var(--aparte-space-3);\\n min-inline-size: 0;\\n color: var(--aparte-text-muted);\\n font-size: var(--aparte-font-size-md);\\n}\\n\\n.aparte-media--gallery > .aparte-media__heading { padding: var(--aparte-space-4) var(--aparte-space-6) var(--aparte-space-5); }\\n\\n.aparte-media__heading-title {\\n overflow: hidden;\\n color: var(--aparte-text);\\n font-weight: var(--aparte-font-weight-semibold);\\n text-overflow: ellipsis;\\n white-space: nowrap;\\n}\\n\\n.aparte-media__heading-title + .aparte-media__count::before {\\n content: \\\"·\\\";\\n margin-inline-end: var(--aparte-space-3);\\n}\\n\\n.aparte-media__count { flex: none; }\\n\\n/* ── Strip: square thumbnails in the kit's carousel ──────────────────── */\\n\\n.aparte-media--strip {\\n display: grid;\\n gap: var(--aparte-space-5);\\n padding: var(--aparte-space-5) var(--aparte-space-6) var(--aparte-space-6);\\n}\\n\\n.aparte-media--strip .aparte-rich-card__carousel {\\n --aparte-rich-card-slide-size: 7rem;\\n --aparte-rich-card-slide-gap: var(--aparte-space-4);\\n}\\n\\n.aparte-media__thumb {\\n display: flex;\\n flex-direction: column;\\n gap: var(--aparte-space-2);\\n min-inline-size: 0;\\n}\\n\\n.aparte-media__thumb > .aparte-media__tile {\\n block-size: auto;\\n aspect-ratio: 1;\\n border-radius: var(--aparte-radius-lg);\\n box-shadow: 0 0 0 var(--aparte-border-width) color-mix(in oklab, var(--aparte-text) 8%, transparent);\\n transition: translate var(--aparte-duration-base, 0.15s) ease, box-shadow var(--aparte-duration-base, 0.15s) ease;\\n}\\n\\n.aparte-media__thumb > .aparte-media__tile:hover {\\n translate: 0 -2px;\\n box-shadow: 0 10px 20px -14px rgb(0 0 0 / 0.55);\\n}\\n\\n.aparte-media__thumb-title {\\n overflow: hidden;\\n color: var(--aparte-text-muted);\\n font-size: var(--aparte-font-size-sm);\\n text-overflow: ellipsis;\\n white-space: nowrap;\\n}\\n\\n/* ── The lightbox ────────────────────────────────────────────────────── */\\n\\n/* Two classes: it must win over the recipe's `--lg` whatever order the sheets land in. */\\n.aparte-dialog.aparte-media__lightbox { --aparte-dialog-width: 56rem; }\\n\\n.aparte-media__lb-head { padding-block-end: var(--aparte-space-4); }\\n\\n.aparte-media__lb-heading {\\n display: flex;\\n flex: 1;\\n align-items: baseline;\\n gap: var(--aparte-space-4);\\n min-inline-size: 0;\\n}\\n\\n.aparte-media__lb-title {\\n flex: 0 1 auto;\\n overflow: hidden;\\n text-overflow: ellipsis;\\n white-space: nowrap;\\n}\\n\\n.aparte-media__lb-counter {\\n flex: none;\\n color: var(--aparte-text-muted);\\n font-size: var(--aparte-font-size-sm);\\n font-variant-numeric: tabular-nums;\\n}\\n\\n/* The stage: the picture on black, as large as the window lets it be. */\\n.aparte-media__lb-body {\\n position: relative;\\n display: grid;\\n min-block-size: 12rem;\\n background: var(--aparte-media-stage);\\n}\\n\\n.aparte-media__lb-stage {\\n display: grid;\\n place-items: center;\\n min-inline-size: 0;\\n max-block-size: var(--aparte-media-lightbox-block);\\n}\\n\\n.aparte-media__lb-picture {\\n position: relative;\\n display: grid;\\n place-items: center;\\n inline-size: 100%;\\n min-block-size: 12rem;\\n}\\n\\n.aparte-media__lb-picture > .aparte-media__placeholder {\\n background: none;\\n color: rgb(255 255 255 / 0.4);\\n}\\n\\n.aparte-media__lb-picture:not([data-loading], [data-broken]) > .aparte-media__placeholder { display: none; }\\n\\n.aparte-media__lb-img {\\n position: relative;\\n z-index: 1;\\n display: block;\\n inline-size: auto;\\n block-size: auto;\\n max-inline-size: 100%;\\n max-block-size: var(--aparte-media-lightbox-block);\\n object-fit: contain;\\n}\\n\\n.aparte-media__lb-video {\\n inline-size: 100%;\\n max-block-size: var(--aparte-media-lightbox-block);\\n}\\n\\n.aparte-media__lb-stage > .aparte-media__external {\\n inline-size: 100%;\\n max-block-size: var(--aparte-media-lightbox-block);\\n border-radius: 0;\\n}\\n\\n.aparte-media__lb-stage > .aparte-media__audio {\\n inline-size: min(100% - var(--aparte-space-8) * 2, 28rem);\\n margin: var(--aparte-space-8);\\n padding: var(--aparte-space-6);\\n border-radius: var(--aparte-radius-xl);\\n background: var(--aparte-surface-1);\\n}\\n\\n.aparte-media__lb-nav {\\n position: absolute;\\n inset-block-start: 50%;\\n z-index: 2;\\n translate: 0 -50%;\\n box-shadow: 0 2px 12px -2px rgb(0 0 0 / 0.45);\\n}\\n\\n.aparte-media__lb-nav--prev { inset-inline-start: var(--aparte-space-5); }\\n.aparte-media__lb-nav--next { inset-inline-end: var(--aparte-space-5); }\\n.aparte-media__lb-nav:dir(rtl) { transform: scaleX(-1); }\\n\\n/* At either end the button stays where it is, and stays focusable — a disabled button\\n would drop the keyboard's focus out of the dialog — but says it leads nowhere. */\\n.aparte-media__lb-nav[aria-disabled=\\\"true\\\"] {\\n opacity: 0.35;\\n cursor: default;\\n}\\n\\n.aparte-media__lb-caption {\\n display: flex;\\n flex-direction: column;\\n gap: var(--aparte-space-1);\\n padding: var(--aparte-space-5) var(--aparte-space-6) var(--aparte-space-6);\\n}\\n\\n.aparte-media__lb-caption[hidden] { display: none; }\\n\\n.aparte-media__lb-caption > .aparte-media__caption { -webkit-line-clamp: 6; }\\n\\n/* ── Loading ─────────────────────────────────────────────────────────── */\\n\\n.aparte-media-card[data-state=\\\"loading\\\"] .aparte-media__sk-frame {\\n block-size: auto;\\n aspect-ratio: 4 / 3;\\n border-radius: var(--aparte-radius-lg);\\n}\\n\\n.aparte-media-card[data-state=\\\"loading\\\"] .aparte-media__sk-cell {\\n block-size: auto;\\n border-radius: 0;\\n}\\n\\n.aparte-media-card[data-state=\\\"loading\\\"] .aparte-media__sk-line { inline-size: 60%; }\\n.aparte-media-card[data-state=\\\"loading\\\"] .aparte-media__sk-line--short { inline-size: 35%; }\\n\\n.aparte-media__sk-row {\\n display: flex;\\n gap: var(--aparte-space-4);\\n overflow: hidden;\\n}\\n\\n.aparte-media-card[data-state=\\\"loading\\\"] .aparte-media__sk-thumb {\\n flex: none;\\n inline-size: 7rem;\\n block-size: 7rem;\\n border-radius: var(--aparte-radius-lg);\\n}\\n\\n/* ── Small screens, and stillness ────────────────────────────────────── */\\n\\n@media (max-width: 380px) {\\n .aparte-media-card { --aparte-media-max-block: 18rem; }\\n .aparte-media--single { padding: var(--aparte-space-3) var(--aparte-space-3) var(--aparte-space-4); }\\n .aparte-media--strip { padding: var(--aparte-space-4) var(--aparte-space-5) var(--aparte-space-5); }\\n .aparte-media--strip .aparte-rich-card__carousel { --aparte-rich-card-slide-size: 6rem; }\\n .aparte-media__play { inline-size: 3rem; block-size: 3rem; }\\n .aparte-media__lb-nav--prev { inset-inline-start: var(--aparte-space-3); }\\n .aparte-media__lb-nav--next { inset-inline-end: var(--aparte-space-3); }\\n}\\n\\n@media (prefers-reduced-motion: reduce) {\\n .aparte-media__frame:hover > .aparte-media__img,\\n .aparte-media__tile:hover > .aparte-media__img,\\n .aparte-media__external:hover > .aparte-media__img { transform: none; }\\n .aparte-media__thumb > .aparte-media__tile:hover { translate: none; }\\n .aparte-media__external:hover .aparte-media__play,\\n .aparte-media__tile:hover .aparte-media__play { scale: none; }\\n .aparte-media__audio[data-playing] .aparte-media__wave { animation: none; }\\n}\\n\"","/**\n * @aparte/plugin-cards/media — a tool's pictures, videos and sounds, drawn as a card: one\n * photo large, a gallery as a mosaic, a row of thumbnails, a lightbox to see each one big.\n *\n * import { setupMediaCard } from '@aparte/plugin-cards/media';\n *\n * setupMediaCard({ tool: 'generate_image' }); // DALL·E's `{ data: [{ url }] }` as it is\n * setupMediaCard({ tool: 'search_photos', map: (r) => r.results }); // Unsplash, Pexels…\n *\n * One medium, several (`[...]`), or `{ title, items: [...] }`. Three variants of one card:\n * `single` (the first medium large, its caption under it), `gallery` (a mosaic, the first\n * larger, \"+3\" on the last tile) and `strip` (square thumbnails that scroll sideways). Every\n * picture opens in a lightbox: the browser's own `<dialog>`, wearing core's `.aparte-dialog`.\n *\n * Nothing here plays a third party: a video is the native `<video>`, a sound the native\n * `<audio>`, and a YouTube or Vimeo page is a poster that LINKS to it — no iframe, no\n * player script. What the browser fetches is what the tool's data names — the medium, its\n * poster — and nothing else: no thumbnail is derived from a video's address, no service\n * is asked about a link. An HLS or DASH stream (`.m3u8`, `.mpd`) plays in the native\n * `<video>` only where the browser says it can (Safari, for HLS); elsewhere it would need a\n * player script, so it is a poster that links out, like a YouTube page.\n */\nimport type { AparteConfig } from '@aparte/core';\nimport { carousel } from '../kit/carousel.js';\nimport { defineCard, type CardContext, type CardSetupOptions } from '../kit/define.js';\nimport { el, glyph, num, safeUrl, str, webUrl, type Loose } from '../kit/dom.js';\nimport { formatNumber, formatUnit } from '../kit/format.js';\nimport { KIT_WORDS, word, type CardWord } from '../kit/strings.js';\nimport { CAMERA, EXPAND, EXTERNAL, FRAMES, PLAY, kindGlyph } from './icons.js';\nimport mediaStyles from './media.css?raw';\n\nexport type MediaKind = 'image' | 'video' | 'audio';\nexport type MediaVariant = 'single' | 'gallery' | 'strip';\n\n/**\n * A medium as `map` returns it. Only `src` is needed; every other field adds something when\n * it is there and nothing when it is not. `readMedia` also reads the usual aliases (listed\n * on each field).\n */\nexport interface MediumInput {\n /** Read from the address's extension or `mimeType` when absent. Also `type`, `mediaType`. */\n kind?: MediaKind;\n /** An `http(s)` address. Also `url`, `link`, `href`, `image`, `imageUrl`, `thumbnail`, Unsplash's `urls.regular`. */\n src: string;\n /** The still shown before a video plays, the cover of a sound, a smaller copy of an image for its tile. Also `thumb`, `preview`. */\n poster?: string;\n title?: string;\n /** Also `description`, and an image model's `revised_prompt`. */\n caption?: string;\n /** Who made it, or where it comes from. Also `author`, `source`, `photographer`, Unsplash's `user.name`. */\n credit?: string;\n /** What the picture shows, for a reader who cannot see it. Also `alt_description`. */\n alt?: string;\n /** With `height`, reserves the picture's shape before it loads — nothing jumps when it does. */\n width?: number;\n height?: number;\n /** In seconds; `\"3:42\"` and ISO 8601 (`\"PT3M42S\"`) are read too. */\n duration?: number | string;\n mimeType?: string;\n /** The page the medium comes from. */\n url?: string;\n}\n\n/** A medium as the card draws it, once read. */\nexport interface Medium {\n kind: MediaKind;\n src: string;\n /**\n * Set when `src` is a video's page on a site that plays it itself (\"YouTube\", \"Vimeo\"):\n * drawn as a poster that links out, never embedded.\n */\n site?: string;\n poster?: string;\n title?: string;\n caption?: string;\n credit?: string;\n alt?: string;\n width?: number;\n height?: number;\n duration?: number;\n mimeType?: string;\n url?: string;\n}\n\n/** The card's data: one medium or several, and the collection's title. */\nexport interface MediaData {\n title?: string;\n items: Medium[];\n}\n\nexport type MediaCardOptions = CardSetupOptions<MediaData, MediaVariant>;\n\nconst M = {\n cardsMediaTitle: { en: 'Media', fr: 'Médias', de: 'Medien', es: 'Multimedia', it: 'Contenuti multimediali', ja: 'メディア', ko: '미디어', pt: 'Mídia', ru: 'Медиа', zh: '媒体' },\n cardsMediaPhotoCount: { en: '{n} photos', fr: '{n} photos', de: '{n} Fotos', es: '{n} fotos', it: '{n} foto', ja: '写真{n}枚', ko: '사진 {n}장', pt: '{n} fotos', ru: 'Фото: {n}', zh: '{n} 张照片' },\n cardsMediaVideoCount: { en: '{n} videos', fr: '{n} vidéos', de: '{n} Videos', es: '{n} vídeos', it: '{n} video', ja: '動画{n}本', ko: '동영상 {n}개', pt: '{n} vídeos', ru: 'Видео: {n}', zh: '{n} 个视频' },\n cardsMediaCount: { en: '{n} items', fr: '{n} éléments', de: '{n} Elemente', es: '{n} elementos', it: '{n} elementi', ja: 'メディア{n}件', ko: '미디어 {n}개', pt: '{n} itens', ru: 'Файлов: {n}', zh: '{n} 个媒体' },\n cardsMediaImage: { en: 'Image', fr: 'Image', de: 'Bild', es: 'Imagen', it: 'Immagine', ja: '画像', ko: '이미지', pt: 'Imagem', ru: 'Изображение', zh: '图片' },\n cardsMediaVideo: { en: 'Video', fr: 'Vidéo', de: 'Video', es: 'Vídeo', it: 'Video', ja: '動画', ko: '동영상', pt: 'Vídeo', ru: 'Видео', zh: '视频' },\n cardsMediaAudio: { en: 'Audio', fr: 'Audio', de: 'Audio', es: 'Audio', it: 'Audio', ja: '音声', ko: '오디오', pt: 'Áudio', ru: 'Аудио', zh: '音频' },\n cardsMediaMore: { en: '{n} more', fr: '{n} de plus', de: '{n} weitere', es: '{n} más', it: 'altri {n}', ja: 'ほか{n}件', ko: '{n}개 더', pt: 'mais {n}', ru: 'ещё {n}', zh: '另外 {n} 个' },\n cardsMediaView: { en: 'View larger', fr: 'Agrandir', de: 'Vergrößern', es: 'Ampliar', it: 'Ingrandisci', ja: '拡大表示', ko: '크게 보기', pt: 'Ampliar', ru: 'Увеличить', zh: '放大查看' },\n cardsMediaClose: { en: 'Close', fr: 'Fermer', de: 'Schließen', es: 'Cerrar', it: 'Chiudi', ja: '閉じる', ko: '닫기', pt: 'Fechar', ru: 'Закрыть', zh: '关闭' },\n cardsMediaWatchOn: { en: 'Watch on {site}', fr: 'Regarder sur {site}', de: 'Auf {site} ansehen', es: 'Ver en {site}', it: 'Guarda su {site}', ja: '{site}で見る', ko: '{site}에서 보기', pt: 'Assistir no {site}', ru: 'Смотреть на {site}', zh: '在 {site} 上观看' },\n} satisfies Record<string, CardWord>;\n\nconst KIND_WORD = { image: 'cardsMediaImage', video: 'cardsMediaVideo', audio: 'cardsMediaAudio' } as const;\n\n/** The most media one card holds; a search that returns a hundred is a page, not a card. */\nconst MAX_MEDIA = 24;\n/** The tiles a gallery's mosaic shows; the last says how many more the lightbox holds. */\nconst GALLERY_TILES = 6;\n\n// ── Reading ──────────────────────────────────────────────────────────────────\n\nconst IMAGE_EXT = /\\.(jpe?g|png|gif|webp|avif|svg|bmp|ico|heic|heif|jxl)$/i;\nconst VIDEO_EXT = /\\.(mp4|m4v|webm|mov|ogv|mkv|m3u8|mpd)$/i;\nconst AUDIO_EXT = /\\.(mp3|m4a|aac|wav|ogg|oga|opus|flac|weba)$/i;\n\n/** A video site whose page plays the video itself: linked to, never embedded. */\nfunction videoSite(url: string): string | undefined {\n const host = new URL(url).hostname.replace(/^(www|m)\\./, '');\n if (host === 'youtu.be' || /(^|\\.)youtube(-nocookie)?\\.com$/.test(host)) return 'YouTube';\n if (/(^|\\.)vimeo\\.com$/.test(host)) return 'Vimeo';\n return undefined;\n}\n\n/** The kind a tool's word or MIME type says: \"photo\", \"image/png\", \"podcast\". */\nfunction kindOfWord(value: unknown): MediaKind | undefined {\n const s = str(value)?.toLowerCase();\n if (!s) return undefined;\n if (/^image\\/|^(image|photo|picture|img|gif|illustration|drawing)s?$/.test(s)) return 'image';\n if (/^video\\/|^application\\/(x-mpegurl|vnd\\.apple\\.mpegurl|dash\\+xml)$|^(video|movie|clip|film)s?$/.test(s)) return 'video';\n if (/^audio\\/|^(audio|sound|music|song|track|podcast|voice|recording)s?$/.test(s)) return 'audio';\n return undefined;\n}\n\n/**\n * A stream's type, when the medium is one: HLS or DASH, by its MIME type or its extension.\n * A playlist, not a file — Chrome and Firefox play neither in a `<video>` without a player\n * script, which this card does not load.\n */\nfunction streamType(m: Medium): string | undefined {\n if (m.mimeType && /^application\\/(x-mpegurl|vnd\\.apple\\.mpegurl|dash\\+xml)$/i.test(m.mimeType)) return m.mimeType;\n const path = new URL(m.src).pathname;\n if (/\\.m3u8$/i.test(path)) return 'application/vnd.apple.mpegurl';\n if (/\\.mpd$/i.test(path)) return 'application/dash+xml';\n return undefined;\n}\n\n/** Whether the browser's own `<video>` plays this medium: asked, for a stream (Safari says \"maybe\" to HLS). */\nfunction playsHere(m: Medium): boolean {\n const type = streamType(m);\n return !type || document.createElement('video').canPlayType(type) !== '';\n}\n\nfunction kindOfUrl(url: string): MediaKind | undefined {\n const path = new URL(url).pathname;\n if (IMAGE_EXT.test(path)) return 'image';\n if (VIDEO_EXT.test(path)) return 'video';\n if (AUDIO_EXT.test(path)) return 'audio';\n return undefined;\n}\n\n/**\n * A length in seconds from what tools write: a number, `\"3:42\"`, `\"1:02:03\"`, ISO 8601's\n * `\"PT3M42S\"` (YouTube's API). `undefined` for anything else.\n */\nexport function readDuration(value: unknown): number | undefined {\n const n = num(value);\n if (n !== undefined) return n >= 0 ? n : undefined;\n const s = str(value);\n if (!s) return undefined;\n const clock = /^(?:(\\d+):)?(\\d{1,2}):(\\d{2})(?:\\.\\d+)?$/.exec(s);\n if (clock) {\n const [h, m, sec] = [Number(clock[1] ?? 0), Number(clock[2]), Number(clock[3])];\n if (sec > 59 || (clock[1] !== undefined && m > 59)) return undefined;\n return h * 3600 + m * 60 + sec;\n }\n const iso = /^PT(?:(\\d+(?:\\.\\d+)?)H)?(?:(\\d+(?:\\.\\d+)?)M)?(?:(\\d+(?:\\.\\d+)?)S)?$/i.exec(s);\n if (iso && (iso[1] || iso[2] || iso[3])) return Number(iso[1] ?? 0) * 3600 + Number(iso[2] ?? 0) * 60 + Number(iso[3] ?? 0);\n return undefined;\n}\n\nfunction textOf(value: unknown): string | undefined {\n if (value && typeof value === 'object') {\n const o = value as Record<string, unknown>;\n return str(o['name'] ?? o['title'] ?? o['text'] ?? o['username']);\n }\n return str(value);\n}\n\n/** An address given as a string, or as an object that holds one — Pexels' `src` is `{ large, original, … }`. */\nfunction linkOf(value: unknown): unknown {\n const first = Array.isArray(value) ? value[0] : value;\n if (!first || typeof first !== 'object') return first;\n const o = first as Record<string, unknown>;\n for (const key of ['url', 'src', 'href', 'uri', 'link', 'regular', 'large2x', 'large', 'medium', 'original', 'full', 'small']) {\n if (typeof o[key] === 'string') return o[key];\n }\n return undefined;\n}\n\n/**\n * An address a medium can be loaded from: `http(s)`, protocol-relative, or a path on this\n * site. A bare word (\"nope\", Google's photo names) would resolve against the page and load\n * something that is not there.\n */\nfunction address(value: unknown): string | undefined {\n const s = str(linkOf(value));\n if (!s || !(/^https?:\\/\\//i.test(s) || s.startsWith('/'))) return undefined;\n // A path on the app's own server (`/uploads/cat.png`) on purpose: media an app serves\n // itself. `webUrl` refuses relative addresses, so this one is resolved here.\n if (s.startsWith('/') && !s.startsWith('//')) {\n const own = safeUrl(s);\n return own && /^https?:/i.test(own) ? own : undefined;\n }\n return webUrl(s.startsWith('//') ? `https:${s}` : s) ?? undefined;\n}\n\nconst positive = (value: unknown) => {\n const n = num(value);\n return n !== undefined && n > 0 ? n : undefined;\n};\n\nfunction readMedium(raw: unknown): Medium | null {\n const value = typeof raw === 'string' ? { src: raw } : raw;\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null;\n const r = value as Loose;\n const urls = r['urls'] && typeof r['urls'] === 'object' ? r['urls'] : {};\n const videoFile = Array.isArray(r['video_files']) ? r['video_files'].find((f: Loose) => address(f?.['link'])) : undefined;\n // Where the medium is, from the key surest of it. `url` and `link` come late: beside a\n // proper source they are the page the medium comes from (Unsplash, Pexels).\n const sources: Array<[string, unknown]> = [\n ['src', r['src']], ['videoFile', videoFile?.['link']], ['image', r['image']], ['imageUrl', r['imageUrl'] ?? r['image_url']],\n ['urls', urls['regular'] ?? urls['full'] ?? urls['small'] ?? urls['raw']], ['contentUrl', r['contentUrl'] ?? r['fileUrl'] ?? r['file_url']],\n ['url', r['url']], ['link', r['link']], ['href', r['href']], ['thumbnail', r['thumbnail']],\n ];\n let src: string | undefined;\n let from = '';\n for (const [key, candidate] of sources) {\n const url = address(candidate);\n if (url) {\n src = url;\n from = key;\n break;\n }\n }\n if (!src) return null;\n\n const page = [r['page'], r['pageUrl'], r['page_url'], r['links']?.['html'], from === 'url' ? undefined : r['url'], from === 'link' ? undefined : r['link'], r['sourceUrl'], r['source_url']]\n .map(address)\n .find((v) => v && v !== src);\n const poster = [r['poster'], r['thumb'], r['preview'], r['previewUrl'], r['preview_url'], urls['small'], from === 'thumbnail' ? undefined : r['thumbnail'], videoFile ? r['image'] : undefined, r['cover'], r['artwork']]\n .map(address)\n .find((v) => v && v !== src);\n const mimeType = str(r['mimeType'] ?? r['mime_type'] ?? r['mime'] ?? r['contentType'] ?? r['content_type'] ?? r['encodingFormat'] ?? videoFile?.['file_type']);\n const site = videoSite(src);\n const kind: MediaKind = site\n ? 'video'\n : kindOfWord(r['kind'] ?? r['mediaType'] ?? r['media_type'] ?? r['type']) ?? kindOfWord(mimeType) ?? (videoFile ? 'video' : undefined) ?? kindOfUrl(src) ?? 'image';\n // A credit that is an address is a page, not a name.\n const creditText = textOf(r['credit'] ?? r['author'] ?? r['photographer'] ?? r['artist'] ?? r['creator'] ?? r['user'] ?? r['source'] ?? r['attribution'] ?? r['channelTitle'] ?? r['channel']);\n const width = positive(r['width'] ?? r['w']);\n const height = positive(r['height'] ?? r['h']);\n const ms = positive(r['duration_ms'] ?? r['durationMs']);\n const duration = ms !== undefined ? ms / 1000 : readDuration(r['duration'] ?? r['length'] ?? r['durationSeconds'] ?? r['duration_seconds']);\n return {\n kind,\n src,\n ...(site ? { site } : {}),\n poster,\n title: textOf(r['title'] ?? r['name']),\n caption: textOf(r['caption'] ?? r['description'] ?? r['revised_prompt'] ?? r['revisedPrompt'] ?? r['summary']),\n credit: creditText && !/^https?:\\/\\//i.test(creditText) ? creditText : undefined,\n alt: str(r['alt'] ?? r['altText'] ?? r['alt_text'] ?? r['alt_description'] ?? r['revised_prompt']),\n // Both or neither: one side alone says nothing of the shape.\n ...(width && height ? { width, height } : {}),\n duration,\n mimeType: mimeType?.includes('/') ? mimeType : undefined,\n url: page,\n };\n}\n\n/**\n * The card's data from what `map` returns: one medium (an object, or its address), an array,\n * or a collection (`{ title?, items | media | images | photos | videos | results | data }` —\n * the last is an image model's answer). At most 24; `null` when not one has an `http(s)` source.\n */\nexport function readMedia(raw: unknown): MediaData | null {\n const value = typeof raw === 'string' ? [raw] : raw;\n if (!value || typeof value !== 'object') return null;\n let title: string | undefined;\n let list: unknown[];\n if (Array.isArray(value)) {\n list = value;\n } else {\n const r = value as Record<string, unknown>;\n const nested = ['items', 'media', 'images', 'photos', 'videos', 'results', 'data', 'files'].map((k) => r[k]).find(Array.isArray);\n if (nested) {\n list = nested;\n title = str(r['title']);\n } else {\n list = [value];\n }\n }\n const items = list.map(readMedium).filter((m): m is Medium => m !== null).slice(0, MAX_MEDIA);\n return items.length ? { ...(title ? { title } : {}), items } : null;\n}\n\n/** A length as a player writes it, in the chat's digits: `3:42`, `1:02:03`. */\nexport function formatDuration(config: AparteConfig, seconds: number): string {\n const total = Math.round(seconds);\n const h = Math.floor(total / 3600);\n const m = Math.floor((total % 3600) / 60);\n const s = total % 60;\n const plain = (v: number) => formatNumber(config, v, { useGrouping: false });\n const two = (v: number) => formatNumber(config, v, { minimumIntegerDigits: 2, useGrouping: false });\n return h ? `${plain(h)}:${two(m)}:${two(s)}` : `${plain(m)}:${two(s)}`;\n}\n\n/** The same length said in words, for a label: \"3 min 42 sec\". */\nfunction spokenDuration(config: AparteConfig, seconds: number): string {\n const total = Math.round(seconds);\n const parts: Array<[number, string]> = [[Math.floor(total / 3600), 'hour'], [Math.floor((total % 3600) / 60), 'minute'], [total % 60, 'second']];\n const said = parts.filter(([v]) => v > 0).map(([v, unit]) => formatUnit(config, v, unit));\n return said.length ? said.join(' ') : formatUnit(config, 0, 'second');\n}\n\n/** ISO 8601 for `<time datetime>`: `PT3M42S`. */\nfunction isoDuration(seconds: number): string {\n const total = Math.round(seconds);\n return `PT${Math.floor(total / 3600)}H${Math.floor((total % 3600) / 60)}M${total % 60}S`;\n}\n\n// ── Drawing ──────────────────────────────────────────────────────────────────\n\nconst kindWord = (config: AparteConfig, kind: MediaKind) => word(config, KIND_WORD[kind], M[KIND_WORD[kind]]);\n\n/** What a medium is called: its title, else what it shows, else \"Image 3\" (\"Image\" alone for one). */\nfunction nameOf(config: AparteConfig, m: Medium, index?: number): string {\n const own = m.title ?? m.alt ?? m.caption;\n if (own) return own;\n return index === undefined ? kindWord(config, m.kind) : `${kindWord(config, m.kind)} ${formatNumber(config, index + 1)}`;\n}\n\n/** A picture's alternative text: what it shows first, then what it is called. */\nconst altOf = (config: AparteConfig, m: Medium, index?: number) => m.alt ?? nameOf(config, m, index);\n\nfunction countLabel(config: AparteConfig, items: Medium[]): string {\n const n = formatNumber(config, items.length);\n if (items.every((m) => m.kind === 'image')) return word(config, 'cardsMediaPhotoCount', M.cardsMediaPhotoCount, { n });\n if (items.every((m) => m.kind === 'video')) return word(config, 'cardsMediaVideoCount', M.cardsMediaVideoCount, { n });\n return word(config, 'cardsMediaCount', M.cardsMediaCount, { n });\n}\n\n/** The kind's glyph, on a soft tint, behind where the picture goes: what shows before it loads, or instead of it. */\nfunction placeholder(kind: MediaKind): HTMLElement {\n return el('span', { class: 'aparte-media__placeholder', attrs: { 'aria-hidden': 'true' } },\n glyph(kindGlyph(kind), 'aparte-rich-card__glyph aparte-media__placeholder-glyph'));\n}\n\n/**\n * A picture in `holder`, lazy and decoded off the main thread. One that fails to load is\n * removed, and the holder's placeholder is what shows. `alt` null: the picture is\n * decoration, its holder is named some other way.\n */\nfunction picture(ctx: CardContext, holder: HTMLElement, src: string, alt: (() => string) | null, className = 'aparte-media__img'): HTMLImageElement {\n const img = el('img', { class: className, attrs: { src, loading: 'lazy', decoding: 'async' } });\n if (alt) ctx.bind(img, alt, 'alt');\n else img.setAttribute('alt', '');\n img.addEventListener('load', () => holder.removeAttribute('data-loading'), { once: true });\n img.addEventListener('error', () => {\n img.remove();\n holder.removeAttribute('data-loading');\n holder.setAttribute('data-broken', '');\n }, { once: true });\n return img;\n}\n\n/**\n * The medium's shape, when its data gives it: the frame is drawn that size before a byte\n * arrives, so nothing below it jumps. Numbers the reader checked, set as properties — never\n * a style string.\n */\nfunction reserve(holder: HTMLElement, m: Medium): void {\n if (!m.width || !m.height) return;\n holder.style.setProperty('--aparte-media-ratio', `${m.width} / ${m.height}`);\n holder.style.setProperty('--aparte-media-ratio-n', String(m.width / m.height));\n holder.setAttribute('data-sized', '');\n}\n\nfunction durationOf(ctx: CardContext, seconds: number, className: string, hidden = false): HTMLElement {\n const node = el('time', { class: className, attrs: { datetime: isoDuration(seconds), 'aria-hidden': hidden ? 'true' : undefined } });\n ctx.bind(node, () => formatDuration(ctx.config, seconds));\n return node;\n}\n\nconst hostOf = (url: string) => new URL(url).hostname.replace(/^www\\./, '');\n\n/** One image, as large as its shape allows; a button that opens it larger still. */\nfunction imageFrame(ctx: CardContext, m: Medium, open: (from: HTMLElement) => void): HTMLElement {\n const frame = el('button', { class: 'aparte-media__frame', attrs: { type: 'button', 'aria-haspopup': 'dialog', 'data-kind': 'image', 'data-loading': '' } });\n reserve(frame, m);\n frame.append(placeholder('image'), picture(ctx, frame, m.src, () => altOf(ctx.config, m)),\n glyph(EXPAND, 'aparte-rich-card__glyph aparte-media__zoom'));\n ctx.bind(frame, () => word(ctx.config, 'cardsMediaView', M.cardsMediaView), 'title');\n frame.addEventListener('click', () => open(frame));\n return frame;\n}\n\n/** A video the browser plays: its own controls, only its length fetched until asked. */\nfunction videoFrame(ctx: CardContext, m: Medium, className = 'aparte-media__frame aparte-media__frame--video'): HTMLElement {\n const frame = el('div', { class: className, attrs: { 'data-kind': 'video' } });\n reserve(frame, m);\n const video = el('video', {\n class: 'aparte-media__video',\n attrs: { controls: true, preload: 'metadata', playsinline: true, src: m.src, poster: m.poster },\n });\n ctx.bind(video, () => nameOf(ctx.config, m), 'aria-label');\n frame.append(video);\n return frame;\n}\n\n/**\n * A YouTube or Vimeo page — or a stream this browser cannot play, linked to the page that\n * plays it when the tool names one, else to the stream itself: its poster, a play disc and\n * the site's name. A link out, never a player.\n */\nfunction externalFrame(ctx: CardContext, m: Medium): HTMLElement {\n const href = m.site ? m.src : (m.url ?? m.src);\n const site = m.site ?? hostOf(href);\n const frame = el('a', {\n class: 'aparte-media__frame aparte-media__external',\n attrs: { href, target: '_blank', rel: 'noopener noreferrer', 'data-kind': 'video' },\n });\n reserve(frame, m);\n frame.append(placeholder('video'));\n if (m.poster) frame.append(picture(ctx, frame, m.poster, null));\n frame.append(\n el('span', { class: 'aparte-media__play', attrs: { 'aria-hidden': 'true' } }, glyph(PLAY, 'aparte-rich-card__glyph aparte-media__play-glyph')),\n el('span', { class: 'aparte-media__site', attrs: { 'aria-hidden': 'true' } }, glyph(EXTERNAL, 'aparte-rich-card__glyph aparte-media__site-glyph'), site),\n );\n if (m.duration !== undefined) frame.append(durationOf(ctx, m.duration, 'aparte-media__duration', true));\n ctx.bind(frame, () => `${nameOf(ctx.config, m)} — ${word(ctx.config, 'cardsMediaWatchOn', M.cardsMediaWatchOn, { site })}`, 'aria-label');\n return frame;\n}\n\n/**\n * A sound: its cover (or a note on a tint), its title and who made it, its length, a\n * waveform that is only decoration — drawn by the sheet, it shows no real level — and the\n * browser's own player under it. The waveform moves while the sound plays.\n */\nfunction audioBlock(ctx: CardContext, m: Medium): HTMLElement {\n const cover = el('span', { class: 'aparte-media__cover', attrs: { 'data-kind': 'audio' } }, placeholder('audio'));\n if (m.poster) cover.append(picture(ctx, cover, m.poster, null));\n const title = el('div', { class: 'aparte-media__audio-title' });\n ctx.bind(title, () => m.title ?? kindWord(ctx.config, 'audio'));\n const text = el('div', { class: 'aparte-media__audio-text' }, title,\n m.credit ? el('div', { class: 'aparte-media__audio-credit', text: m.credit }) : null);\n const head = el('div', { class: 'aparte-media__audio-head' }, cover, text,\n m.duration !== undefined ? durationOf(ctx, m.duration, 'aparte-media__audio-length') : null);\n const audio = el('audio', { class: 'aparte-media__player', attrs: { controls: true, preload: 'metadata', src: m.src } });\n ctx.bind(audio, () => nameOf(ctx.config, m), 'aria-label');\n const block = el('div', { class: 'aparte-media__audio', attrs: { 'data-kind': 'audio' } },\n head, el('span', { class: 'aparte-media__wave', attrs: { 'aria-hidden': 'true' } }), audio);\n audio.addEventListener('play', () => block.setAttribute('data-playing', ''));\n for (const stop of ['pause', 'ended', 'emptied']) audio.addEventListener(stop, () => block.removeAttribute('data-playing'));\n return block;\n}\n\n/**\n * Under a medium: its title, its caption, and a line with who made it and the page it comes\n * from. `withTitle` false where the title is already drawn (a sound's header, the lightbox's).\n */\nfunction captionOf(m: Medium, withTitle: boolean, tag: 'figcaption' | 'div' = 'figcaption'): HTMLElement | null {\n const title = withTitle && m.title ? el('div', { class: 'aparte-media__title', text: m.title }) : null;\n const caption = m.caption && m.caption !== m.title ? el('p', { class: 'aparte-media__caption', text: m.caption }) : null;\n const credit = m.credit && m.kind !== 'audio'\n ? el('span', { class: 'aparte-media__credit' }, glyph(m.kind === 'image' ? CAMERA : kindGlyph(m.kind), 'aparte-rich-card__glyph aparte-media__line-glyph'), el('span', { text: m.credit }))\n : null;\n const source = m.url\n ? el('a', { class: 'aparte-media__source', attrs: { href: m.url, target: '_blank', rel: 'noopener noreferrer' } },\n el('span', { text: hostOf(m.url) }), glyph(EXTERNAL, 'aparte-rich-card__glyph aparte-media__line-glyph'))\n : null;\n const meta = credit || source ? el('div', { class: 'aparte-media__meta' }, credit, source) : null;\n if (!title && !caption && !meta) return null;\n return el(tag, { class: 'aparte-media__figcaption' }, title, caption, meta);\n}\n\n/** What a medium shows where it is drawn whole — on the card alone, or on the lightbox's stage. */\nfunction stageOf(ctx: CardContext, m: Medium, open: ((from: HTMLElement) => void) | null, index?: number): HTMLElement {\n if (m.kind === 'audio') return audioBlock(ctx, m);\n if (m.site || (m.kind === 'video' && !playsHere(m))) return externalFrame(ctx, m);\n if (m.kind === 'video') return videoFrame(ctx, m, open ? undefined : 'aparte-media__lb-video');\n if (open) return imageFrame(ctx, m, open);\n // In the lightbox: no lazy loading, it is the one picture on screen.\n const box = el('div', { class: 'aparte-media__lb-picture', attrs: { 'data-loading': '' } }, placeholder('image'));\n const img = picture(ctx, box, m.src, () => altOf(ctx.config, m, index), 'aparte-media__lb-img');\n img.removeAttribute('loading');\n box.append(img);\n return box;\n}\n\n/** A medium's tile in a mosaic or a row: a button that opens the lightbox on it. */\nfunction tileOf(ctx: CardContext, m: Medium, index: number, more: number, open: (index: number, from: HTMLElement) => void): HTMLButtonElement {\n const tile = el('button', { class: 'aparte-media__tile', attrs: { type: 'button', 'aria-haspopup': 'dialog', 'data-kind': m.kind } });\n tile.append(placeholder(m.kind));\n // An image's tile may load its smaller copy; a video's and a sound's show their still, if any.\n const still = m.kind === 'image' ? (m.poster ?? m.src) : m.poster;\n if (still) tile.append(picture(ctx, tile, still, null, 'aparte-media__img'));\n if (m.kind === 'video') {\n tile.append(el('span', { class: 'aparte-media__play aparte-media__play--sm', attrs: { 'aria-hidden': 'true' } },\n glyph(PLAY, 'aparte-rich-card__glyph aparte-media__play-glyph')));\n }\n if (m.kind === 'audio' && m.title) tile.append(el('span', { class: 'aparte-media__tile-title', attrs: { 'aria-hidden': 'true' }, text: m.title }));\n if (m.duration !== undefined && m.kind !== 'image') tile.append(durationOf(ctx, m.duration, 'aparte-media__duration', true));\n if (more > 0) {\n const plus = el('span', { class: 'aparte-media__more', attrs: { 'aria-hidden': 'true' } });\n ctx.bind(plus, () => `+${formatNumber(ctx.config, more)}`);\n tile.append(plus);\n }\n // The tile's name says everything its face shows: what it is, how long, how many behind it.\n ctx.bind(tile, () => {\n const parts = [nameOf(ctx.config, m, index)];\n if (m.kind !== 'image' && (m.title ?? m.alt ?? m.caption)) parts.push(kindWord(ctx.config, m.kind));\n if (m.duration !== undefined && m.kind !== 'image') parts.push(spokenDuration(ctx.config, m.duration));\n if (more > 0) parts.push(word(ctx.config, 'cardsMediaMore', M.cardsMediaMore, { n: formatNumber(ctx.config, more) }));\n return parts.join(', ');\n }, 'aria-label');\n tile.addEventListener('click', () => open(index, tile));\n return tile;\n}\n\n/** The collection's name line: its title, and how many there are. */\nfunction headingOf(ctx: CardContext, data: MediaData): HTMLElement {\n const count = el('span', { class: 'aparte-media__count' });\n ctx.bind(count, () => countLabel(ctx.config, data.items));\n return el('div', { class: 'aparte-media__heading' }, glyph(FRAMES, 'aparte-rich-card__glyph aparte-media__line-glyph'),\n data.title ? el('span', { class: 'aparte-media__heading-title', text: data.title }) : null, count);\n}\n\nlet lightboxes = 0;\n\n/** A text kept in the chat's language, and set again by `update()` when what it reads has moved. */\nfunction live(ctx: CardContext, target: Element, compute: () => string, attr?: string): () => void {\n ctx.bind(target, compute, attr);\n return () => (attr ? target.setAttribute(attr, compute()) : (target.textContent = compute()));\n}\n\n/**\n * The lightbox: one `<dialog>` per card, made the first time it is opened, shown modally —\n * the browser traps the focus in it, closes it on Escape and puts the page behind it out of\n * reach; core's `.aparte-dialog` draws it and closes it from its backdrop. Arrows step\n * through the media (mirrored in a right-to-left page), Home and End jump to the ends, and\n * the focus goes back to the tile that opened it. Returns the function that opens it.\n */\nfunction lightbox(ctx: CardContext, items: Medium[], host: () => HTMLElement): (index: number, from: HTMLElement) => void {\n let dialog: HTMLDialogElement | null = null;\n let index = 0;\n let opener: HTMLElement | null = null;\n let stage!: HTMLElement;\n let caption!: HTMLElement;\n const refresh: Array<() => void> = [];\n const navs: Array<[HTMLButtonElement, -1 | 1]> = [];\n const many = items.length > 1;\n\n function shut(): void {\n if (!dialog) return;\n if (typeof dialog.close === 'function') {\n dialog.close();\n } else {\n dialog.removeAttribute('open');\n dialog.dispatchEvent(new Event('close'));\n }\n }\n\n function show(i: number): void {\n index = Math.max(0, Math.min(items.length - 1, i));\n const m = items[index]!;\n stage.setAttribute('data-kind', m.kind);\n stage.replaceChildren(stageOf(ctx, m, null, many ? index : undefined));\n caption.replaceChildren(...(captionOf(m, false, 'div')?.childNodes ?? []));\n caption.hidden = !caption.childNodes.length;\n for (const update of refresh) update();\n // `aria-disabled`, not `disabled`: a disabled button drops the keyboard's focus.\n for (const [button, dir] of navs) button.setAttribute('aria-disabled', String(dir < 0 ? index === 0 : index === items.length - 1));\n }\n\n /** Step to `i`, if it is another medium: the ends do not redraw what is on the stage. */\n function go(i: number): void {\n if (i >= 0 && i < items.length && i !== index) show(i);\n }\n\n function navButton(dir: -1 | 1): HTMLButtonElement {\n const button = el('button', {\n class: `aparte-btn aparte-btn--icon aparte-btn--circle aparte-btn--surface aparte-media__lb-nav aparte-media__lb-nav--${dir < 0 ? 'prev' : 'next'}`,\n attrs: { type: 'button' },\n });\n button.innerHTML = ctx.config.getIcon(dir < 0 ? 'prevBranch' : 'nextBranch');\n ctx.bind(button, () => word(ctx.config, dir < 0 ? 'cardsPrevious' : 'cardsNext', dir < 0 ? KIT_WORDS.cardsPrevious : KIT_WORDS.cardsNext), 'aria-label');\n button.addEventListener('click', () => go(index + dir));\n navs.push([button, dir]);\n return button;\n }\n\n function onKey(event: KeyboardEvent): void {\n // A player's own keys seek and turn the volume: they are not ours to take.\n if ((event.target as Element | null)?.closest?.('video, audio')) return;\n const rtl = typeof getComputedStyle === 'function' && getComputedStyle(dialog!).direction === 'rtl';\n let to: number | undefined;\n if (event.key === 'ArrowRight' || event.key === 'ArrowLeft') to = index + (event.key === 'ArrowRight' ? 1 : -1) * (rtl ? -1 : 1);\n else if (event.key === 'Home') to = 0;\n else if (event.key === 'End') to = items.length - 1;\n else if (event.key === 'Escape' && typeof dialog!.showModal !== 'function') {\n // A browser without a modal dialog has no Escape of its own.\n event.preventDefault();\n shut();\n return;\n }\n if (to === undefined) return;\n event.preventDefault();\n go(to);\n }\n\n function build(): HTMLDialogElement {\n const id = `aparte-media-lightbox-${++lightboxes}`;\n const title = el('h2', { class: 'aparte-dialog__title aparte-media__lb-title', attrs: { id } });\n refresh.push(live(ctx, title, () => nameOf(ctx.config, items[index]!, many ? index : undefined)));\n const heading = el('div', { class: 'aparte-media__lb-heading' }, title);\n if (many) {\n const counter = el('span', { class: 'aparte-media__lb-counter' });\n refresh.push(live(ctx, counter, () => word(ctx.config, 'cardsSlide', KIT_WORDS.cardsSlide, {\n n: formatNumber(ctx.config, index + 1), total: formatNumber(ctx.config, items.length),\n })));\n heading.append(counter);\n }\n const close = el('button', { class: 'aparte-btn aparte-btn--icon aparte-btn--sm aparte-btn--ghost aparte-dialog__close aparte-media__lb-close', attrs: { type: 'button' } });\n close.innerHTML = ctx.config.getIcon('close');\n ctx.bind(close, () => word(ctx.config, 'cardsMediaClose', M.cardsMediaClose), 'aria-label');\n close.addEventListener('click', shut);\n stage = el('div', { class: 'aparte-media__lb-stage' });\n caption = el('div', { class: 'aparte-media__lb-caption' });\n const body = el('div', { class: 'aparte-media__lb-body' }, stage);\n if (many) body.append(navButton(-1), navButton(1));\n const node = el('dialog', { class: 'aparte-dialog aparte-dialog--lg aparte-media__lightbox', attrs: { 'aria-labelledby': id } },\n el('div', { class: 'aparte-dialog__header aparte-media__lb-head' }, heading, close), body, caption);\n node.addEventListener('keydown', onKey);\n node.addEventListener('close', () => {\n // A video left playing behind a closed dialog would go on talking.\n for (const media of stage.querySelectorAll<HTMLMediaElement>('video, audio')) if (!media.paused) media.pause();\n opener?.focus();\n });\n return node;\n }\n\n return (i, from) => {\n if (!dialog) {\n dialog = build();\n host().append(dialog);\n }\n opener = from;\n show(i);\n if (typeof dialog.showModal === 'function') {\n if (!dialog.open) dialog.showModal();\n } else {\n dialog.setAttribute('open', '');\n }\n };\n}\n\nfunction render(data: MediaData, ctx: CardContext<MediaVariant>): Node[] {\n const [first] = data.items as [Medium];\n let root: HTMLElement | undefined;\n const open = lightbox(ctx, data.items, () => root!);\n if (data.items.length === 1) {\n // One medium is drawn large whatever the variant: a mosaic or a row of one is a frame.\n root = el('figure', { class: 'aparte-media aparte-media--single', attrs: { 'data-kind': first.kind } },\n stageOf(ctx, first, (from) => open(0, from)), captionOf(first, first.kind !== 'audio'));\n return [root];\n }\n if (ctx.variant === 'strip') {\n const slides = data.items.map((m, i) => el('div', { class: 'aparte-media__thumb' }, tileOf(ctx, m, i, 0, open),\n m.title ? el('span', { class: 'aparte-media__thumb-title', attrs: { 'aria-hidden': 'true' }, text: m.title } ) : null));\n root = el('div', { class: 'aparte-media aparte-media--strip' }, headingOf(ctx, data),\n carousel(ctx, slides, { label: () => data.title ?? countLabel(ctx.config, data.items) }));\n return [root];\n }\n // Several media in `single` are a gallery too: drawing the first and dropping the rest\n // would hide what the tool returned.\n const shown = data.items.slice(0, GALLERY_TILES);\n const more = data.items.length - shown.length;\n const grid = el('ul', { class: 'aparte-media__grid', attrs: { 'data-count': shown.length } },\n ...shown.map((m, i) => el('li', { class: 'aparte-media__cell' }, tileOf(ctx, m, i, i === shown.length - 1 ? more : 0, open))));\n ctx.bind(grid, () => data.title ?? countLabel(ctx.config, data.items), 'aria-label');\n root = el('div', { class: 'aparte-media aparte-media--gallery' }, grid, headingOf(ctx, data));\n return [root];\n}\n\nfunction skeleton(partial: Partial<MediaData> | null, ctx: CardContext<MediaVariant>): Node[] {\n const block = (cls: string, kind: 'text' | 'rect' = 'rect') =>\n el('span', { class: `aparte-skeleton aparte-skeleton--${kind} ${cls}`, attrs: { 'aria-hidden': 'true' } });\n const known = partial?.title ?? partial?.items?.[0]?.title;\n if (ctx.variant === 'gallery') {\n return [el('div', { class: 'aparte-media aparte-media--gallery' },\n el('div', { class: 'aparte-media__grid', attrs: { 'data-count': 4 } }, ...Array.from({ length: 4 }, () => block('aparte-media__sk-cell'))),\n el('div', { class: 'aparte-media__heading' }, known ? el('span', { class: 'aparte-media__heading-title', text: known }) : block('aparte-media__sk-line', 'text')))];\n }\n if (ctx.variant === 'strip') {\n return [el('div', { class: 'aparte-media aparte-media--strip' },\n el('div', { class: 'aparte-media__heading' }, known ? el('span', { class: 'aparte-media__heading-title', text: known }) : block('aparte-media__sk-line', 'text')),\n el('div', { class: 'aparte-media__sk-row' }, ...Array.from({ length: 4 }, () => block('aparte-media__sk-thumb'))))];\n }\n return [el('div', { class: 'aparte-media aparte-media--single' }, block('aparte-media__sk-frame'),\n el('div', { class: 'aparte-media__figcaption' },\n known ? el('div', { class: 'aparte-media__title', text: known }) : block('aparte-media__sk-line', 'text'),\n block('aparte-media__sk-line aparte-media__sk-line--short', 'text')))];\n}\n\nfunction label(data: Partial<MediaData> | null, ctx: CardContext): string {\n const items = data?.items ?? [];\n if (items.length === 1) return nameOf(ctx.config, items[0]!);\n if (items.length > 1) return data!.title ?? countLabel(ctx.config, items);\n return data?.title ?? word(ctx.config, 'cardsMediaTitle', M.cardsMediaTitle);\n}\n\n/** One line a medium: \"Sunset over Lille — Jane Doe (3:42): https://…\", the address a model can use. */\nfunction fallback(data: MediaData, ctx: CardContext): string {\n const lines = data.items.map((m, i) => {\n const name = nameOf(ctx.config, m, data.items.length > 1 ? i : undefined);\n const credit = m.credit ? ` — ${m.credit}` : '';\n const length = m.duration !== undefined ? ` (${formatDuration(ctx.config, m.duration)})` : '';\n return `${name}${credit}${length}: ${m.src}`;\n });\n return data.title ? `${data.title}:\\n${lines.join('\\n')}` : lines.join('\\n');\n}\n\n/** What a running call already says: the prompt an image is made from, the search it answers. */\nfunction partial(input: Record<string, unknown>): Partial<MediaData> | null {\n const title = str(input['prompt'] ?? input['query'] ?? input['q'] ?? input['search'] ?? input['title']);\n return title ? { title } : null;\n}\n\n/** The media card — for `mediaCard.segment(data)`. Plug it in with `setupMediaCard`. */\nexport const mediaCard = defineCard<MediaData, MediaVariant>({\n name: 'media',\n variants: ['single', 'gallery', 'strip'],\n styles: mediaStyles,\n icon: FRAMES,\n normalize: readMedia,\n render,\n skeleton,\n label,\n fallback,\n partial,\n});\n\n/** Draw a tool's media as this card — returns the function that stops it. */\nexport function setupMediaCard(options: MediaCardOptions = {}): () => void {\n return mediaCard.setup(options);\n}\n"],"names":["partial"],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The order card's glyphs: the bag and the calendar that say which kind of record it is,
|
|
3
|
+
* the box a line shows without a photo, the small marks before a fact (card, van, pin,
|
|
4
|
+
* people, person, house, info), the chevron of a row that links out, and the seal of the
|
|
5
|
+
* confirmation — a disc with a mark drawn on it.
|
|
6
|
+
*
|
|
7
|
+
* Drawn on a 24 × 24 grid in `currentColor` strokes (the seal on 64 × 64), so the sheet
|
|
8
|
+
* colours them through `color` and a theme follows. Constants of this package — the only
|
|
9
|
+
* markup the card parses.
|
|
10
|
+
*/
|
|
11
|
+
/** A shopping bag: an order, and the card's glyph on its one-line states. */
|
|
12
|
+
export declare const BAG: string;
|
|
13
|
+
/** A calendar page: a booking. */
|
|
14
|
+
export declare const CALENDAR: string;
|
|
15
|
+
/** A parcel: the thumbnail of a line that has no photo, or whose photo failed. */
|
|
16
|
+
export declare const BOX: string;
|
|
17
|
+
/** A delivery van, before the delivery row. */
|
|
18
|
+
export declare const TRUCK: string;
|
|
19
|
+
/** A bank card, before the payment row. */
|
|
20
|
+
export declare const CARD: string;
|
|
21
|
+
/** A pin, before where a booking takes place. */
|
|
22
|
+
export declare const PIN: string;
|
|
23
|
+
/** Two people, before the number of guests. */
|
|
24
|
+
export declare const PEOPLE: string;
|
|
25
|
+
/** One person, before the customer. */
|
|
26
|
+
export declare const PERSON: string;
|
|
27
|
+
/** A house, before the delivery address. */
|
|
28
|
+
export declare const HOUSE: string;
|
|
29
|
+
/** A dot in a circle, before a fact of the tool's own. */
|
|
30
|
+
export declare const INFO: string;
|
|
31
|
+
/** A chevron at the end of a row that opens the order. Mirrored by the sheet in RTL. */
|
|
32
|
+
export declare const CHEVRON: string;
|
|
33
|
+
/** The arrow between a stay's two dates. Mirrored by the sheet in RTL. */
|
|
34
|
+
export declare const ARROW: string;
|
|
35
|
+
/** The marks a seal can carry, by its tone. Each is one path, drawn on in the sheet. */
|
|
36
|
+
declare const MARKS: {
|
|
37
|
+
readonly success: "M22 32.5l7 7 13.5-14.5";
|
|
38
|
+
readonly info: "M32 22v10.5l7 4.5";
|
|
39
|
+
readonly danger: "M24.5 24.5l15 15M39.5 24.5l-15 15";
|
|
40
|
+
readonly neutral: "M25.5 27.5h9.5a6.5 6.5 0 0 1 0 13h-8M30 22.5l-5 5 5 5";
|
|
41
|
+
};
|
|
42
|
+
export type SealTone = keyof typeof MARKS;
|
|
43
|
+
/**
|
|
44
|
+
* The confirmation's seal: a soft halo, a disc in the status' tone, and its mark. The mark
|
|
45
|
+
* has `pathLength="1"` so the sheet draws it on with one dash, whatever its real length.
|
|
46
|
+
*/
|
|
47
|
+
export declare function seal(tone: SealTone): string;
|
|
48
|
+
export {};
|
|
49
|
+
//# sourceMappingURL=icons.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../../src/order/icons.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,6EAA6E;AAC7E,eAAO,MAAM,GAAG,QAAiI,CAAC;AAElJ,kCAAkC;AAClC,eAAO,MAAM,QAAQ,QAAuK,CAAC;AAE7L,kFAAkF;AAClF,eAAO,MAAM,GAAG,QAAmH,CAAC;AAEpI,+CAA+C;AAC/C,eAAO,MAAM,KAAK,QAAyJ,CAAC;AAE5K,2CAA2C;AAC3C,eAAO,MAAM,IAAI,QAAqG,CAAC;AAEvH,iDAAiD;AACjD,eAAO,MAAM,GAAG,QAAqH,CAAC;AAEtI,+CAA+C;AAC/C,eAAO,MAAM,MAAM,QAA8K,CAAC;AAElM,uCAAuC;AACvC,eAAO,MAAM,MAAM,QAAgG,CAAC;AAEpH,4CAA4C;AAC5C,eAAO,MAAM,KAAK,QAA2E,CAAC;AAE9F,0DAA0D;AAC1D,eAAO,MAAM,IAAI,QAAkH,CAAC;AAEpI,wFAAwF;AACxF,eAAO,MAAM,OAAO,QAAoC,CAAC;AAEzD,0EAA0E;AAC1E,eAAO,MAAM,KAAK,QAAuD,CAAC;AAE1E,wFAAwF;AACxF,QAAA,MAAM,KAAK;;;;;CAKD,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,KAAK,CAAC;AAE1C;;;GAGG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAE3C"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @aparte/plugin-cards/order — an order or a booking, drawn as its receipt.
|
|
3
|
+
*
|
|
4
|
+
* import { setupOrderCard } from '@aparte/plugin-cards/order';
|
|
5
|
+
*
|
|
6
|
+
* setupOrderCard({ tool: 'get_order', variant: 'receipt' });
|
|
7
|
+
*
|
|
8
|
+
* The summary every messaging platform settled on for a purchase or a reservation: what
|
|
9
|
+
* was bought (or booked), what it cost, where it stands, and where it is going. Three
|
|
10
|
+
* variants of one card: `receipt` (the whole record — lines, totals, payment, delivery,
|
|
11
|
+
* address), `compact` (one row, for a list of past orders) and `confirmation` (the moment
|
|
12
|
+
* it is placed — a seal, a headline, the few facts that matter, the next step).
|
|
13
|
+
*
|
|
14
|
+
* Your tool keeps its own fields: `readOrder` already reads the usual spellings — a
|
|
15
|
+
* Shopify or WooCommerce order (`line_items`, `order_number`, `financial_status`,
|
|
16
|
+
* `shipping_address`), a Stripe Checkout session (`amount_total`, `total_details`; its
|
|
17
|
+
* amounts are in cents, so say `minorUnits: true` — the card does not guess) — and `map`
|
|
18
|
+
* covers the rest.
|
|
19
|
+
*/
|
|
20
|
+
import type { AparteConfig } from '@aparte/core';
|
|
21
|
+
import { type CardSetupOptions } from '../kit/define.js';
|
|
22
|
+
export type OrderVariant = 'receipt' | 'compact' | 'confirmation';
|
|
23
|
+
export type OrderKind = 'order' | 'booking';
|
|
24
|
+
/** Where an order or a booking stands; anything else a tool writes is shown as it wrote it. */
|
|
25
|
+
export type OrderStatus = 'pending' | 'confirmed' | 'processing' | 'shipped' | 'delivered' | 'cancelled' | 'refunded' | 'failed';
|
|
26
|
+
/** A moment a tool gave: an ISO string, an epoch, a `Date`. */
|
|
27
|
+
export type OrderDateInput = string | number | Date;
|
|
28
|
+
/** One line as `map` returns it. Only `title` is needed. */
|
|
29
|
+
export interface OrderLineInput {
|
|
30
|
+
title: string;
|
|
31
|
+
/** The variant: "Blue · M". */
|
|
32
|
+
subtitle?: string;
|
|
33
|
+
/** An `http(s)` URL, or `{ src, alt }`. */
|
|
34
|
+
image?: string | {
|
|
35
|
+
src: string;
|
|
36
|
+
alt?: string;
|
|
37
|
+
};
|
|
38
|
+
/** 1 when missing. 0 is kept — a line no longer bought — and left out of the subtotal. */
|
|
39
|
+
quantity?: number;
|
|
40
|
+
/** The unit price: `12`, `"12.00"`, `"12,00 €"`. */
|
|
41
|
+
price?: number | string;
|
|
42
|
+
/** The line's total, before the order's discount; `quantity × price` when missing. */
|
|
43
|
+
total?: number | string;
|
|
44
|
+
}
|
|
45
|
+
/** What `map` returns. Every field adds a part when it is there and nothing when it is not. */
|
|
46
|
+
export interface OrderInput {
|
|
47
|
+
/** `order` by default. */
|
|
48
|
+
kind?: OrderKind;
|
|
49
|
+
title?: string;
|
|
50
|
+
/**
|
|
51
|
+
* The `confirmation` variant's headline, when the status's own sentence ("Order being
|
|
52
|
+
* prepared") is not what happened: "Return started", "Exchange requested".
|
|
53
|
+
*/
|
|
54
|
+
headline?: string;
|
|
55
|
+
/** The order or booking number. */
|
|
56
|
+
reference?: string | number;
|
|
57
|
+
status?: OrderStatus | string;
|
|
58
|
+
/**
|
|
59
|
+
* When it was placed. For a booking with no start of its own, it is the booking's day —
|
|
60
|
+
* with a `time` beside it (`'19:30'`) when the tool gives one apart.
|
|
61
|
+
*/
|
|
62
|
+
date?: OrderDateInput;
|
|
63
|
+
items?: OrderLineInput[];
|
|
64
|
+
/** An ISO 4217 code (`EUR`) or a usual symbol (`€`). */
|
|
65
|
+
currency?: string;
|
|
66
|
+
/** Summed from the lines when missing (and every line has a total). */
|
|
67
|
+
subtotal?: number | string;
|
|
68
|
+
/** Shown taken off: `10` reads "−€10.00". */
|
|
69
|
+
discount?: number | string;
|
|
70
|
+
/** `0` reads "Free". */
|
|
71
|
+
shipping?: number | string;
|
|
72
|
+
tax?: number | string;
|
|
73
|
+
/** Computed when missing: subtotal − discount + shipping + tax. A tool's own total always wins. */
|
|
74
|
+
total?: number | string;
|
|
75
|
+
/** "Visa •••• 4242", or `{ brand, last4 }`. */
|
|
76
|
+
payment?: string | {
|
|
77
|
+
brand?: string;
|
|
78
|
+
last4?: string;
|
|
79
|
+
};
|
|
80
|
+
/** Several lines; `\n` keeps them apart. Or `{ name, email }`. */
|
|
81
|
+
customer?: string | Record<string, unknown>;
|
|
82
|
+
/** Several lines, or an address object (`address1`, `city`, `zip`, `country`…). */
|
|
83
|
+
shippingAddress?: string | Record<string, unknown>;
|
|
84
|
+
delivery?: {
|
|
85
|
+
/** A date, a range `{ from, to }`, or words ("2–4 business days"). */
|
|
86
|
+
estimate?: OrderDateInput | {
|
|
87
|
+
from: OrderDateInput;
|
|
88
|
+
to?: OrderDateInput;
|
|
89
|
+
};
|
|
90
|
+
carrier?: string;
|
|
91
|
+
trackingNumber?: string;
|
|
92
|
+
/** `http(s)` only: the "Track" button opens it. */
|
|
93
|
+
trackingUrl?: string;
|
|
94
|
+
};
|
|
95
|
+
/** Rows of your own: `{ label: 'Room', value: 'Deluxe' }`. */
|
|
96
|
+
facts?: Array<{
|
|
97
|
+
label: string;
|
|
98
|
+
value: string | number;
|
|
99
|
+
}>;
|
|
100
|
+
/** For `kind: 'booking'`. */
|
|
101
|
+
booking?: {
|
|
102
|
+
start?: OrderDateInput;
|
|
103
|
+
end?: OrderDateInput;
|
|
104
|
+
location?: string;
|
|
105
|
+
guests?: number;
|
|
106
|
+
};
|
|
107
|
+
/** The order's page; `http(s)` only. */
|
|
108
|
+
url?: string;
|
|
109
|
+
/** Every amount is in the currency's minor unit (Stripe's cents): divided before it is shown. */
|
|
110
|
+
minorUnits?: boolean;
|
|
111
|
+
}
|
|
112
|
+
/** A moment, and whether it carries a time — a hotel's "2026-11-07" is a day, not midnight. */
|
|
113
|
+
export interface OrderWhen {
|
|
114
|
+
date: Date;
|
|
115
|
+
allDay: boolean;
|
|
116
|
+
}
|
|
117
|
+
export interface OrderLine {
|
|
118
|
+
title: string;
|
|
119
|
+
subtitle?: string;
|
|
120
|
+
image?: {
|
|
121
|
+
src: string;
|
|
122
|
+
alt?: string;
|
|
123
|
+
};
|
|
124
|
+
quantity: number;
|
|
125
|
+
price?: number;
|
|
126
|
+
total?: number;
|
|
127
|
+
}
|
|
128
|
+
export interface OrderFact {
|
|
129
|
+
label: string;
|
|
130
|
+
value: string;
|
|
131
|
+
}
|
|
132
|
+
/** The data as the card draws it, once read. */
|
|
133
|
+
export interface OrderData {
|
|
134
|
+
kind: OrderKind;
|
|
135
|
+
title?: string;
|
|
136
|
+
headline?: string;
|
|
137
|
+
reference?: string;
|
|
138
|
+
status?: {
|
|
139
|
+
status: OrderStatus;
|
|
140
|
+
} | {
|
|
141
|
+
text: string;
|
|
142
|
+
};
|
|
143
|
+
date?: Date;
|
|
144
|
+
/** At most fifty; `lineCount` says how many the tool gave. */
|
|
145
|
+
items: OrderLine[];
|
|
146
|
+
lineCount: number;
|
|
147
|
+
/** The quantities added up — "3 items" for one line of two and one of one. */
|
|
148
|
+
itemCount: number;
|
|
149
|
+
currency?: string;
|
|
150
|
+
subtotal?: number;
|
|
151
|
+
/** Positive: what was taken off. */
|
|
152
|
+
discount?: number;
|
|
153
|
+
shipping?: number;
|
|
154
|
+
tax?: number;
|
|
155
|
+
total?: number;
|
|
156
|
+
payment?: string;
|
|
157
|
+
customer?: string;
|
|
158
|
+
shippingAddress?: string;
|
|
159
|
+
delivery?: {
|
|
160
|
+
estimate?: {
|
|
161
|
+
from: OrderWhen;
|
|
162
|
+
to?: OrderWhen;
|
|
163
|
+
} | {
|
|
164
|
+
text: string;
|
|
165
|
+
};
|
|
166
|
+
carrier?: string;
|
|
167
|
+
trackingNumber?: string;
|
|
168
|
+
trackingUrl?: string;
|
|
169
|
+
};
|
|
170
|
+
facts: OrderFact[];
|
|
171
|
+
booking?: {
|
|
172
|
+
start?: OrderWhen;
|
|
173
|
+
end?: OrderWhen;
|
|
174
|
+
location?: string;
|
|
175
|
+
guests?: number;
|
|
176
|
+
};
|
|
177
|
+
url?: string;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* A moment from what a tool wrote — or `undefined`. Strict on purpose: `new Date('Nov 7')`
|
|
181
|
+
* reads 2001 in V8, so a string is a date only when it is ISO. A bare day (`2026-11-07`)
|
|
182
|
+
* is that day in the reader's calendar, not midnight in London, and carries no time.
|
|
183
|
+
*/
|
|
184
|
+
export declare function readWhen(value: unknown): OrderWhen | undefined;
|
|
185
|
+
/**
|
|
186
|
+
* The card's data from the loose shape `map` returns, or `null` when it has nothing to
|
|
187
|
+
* draw — no line, no number, no total, no booking date, no title.
|
|
188
|
+
*/
|
|
189
|
+
export declare function readOrder(raw: unknown): OrderData | null;
|
|
190
|
+
/** Nights between two days; 0 for a booking that starts and ends on the same one. */
|
|
191
|
+
export declare function nightsOf(from: Date, to: Date): number;
|
|
192
|
+
export declare const orderCard: {
|
|
193
|
+
name: string;
|
|
194
|
+
variants: readonly OrderVariant[];
|
|
195
|
+
setup: (options?: CardSetupOptions<OrderData, OrderVariant>) => () => void;
|
|
196
|
+
segment: (raw: unknown, options?: {
|
|
197
|
+
id?: string;
|
|
198
|
+
variant?: OrderVariant | undefined;
|
|
199
|
+
config?: AparteConfig;
|
|
200
|
+
}) => import("../index.js").AparteCardSegment;
|
|
201
|
+
};
|
|
202
|
+
/** Draw a tool's order or booking as this card — returns the function that stops it. */
|
|
203
|
+
export declare function setupOrderCard(options?: CardSetupOptions<OrderData, OrderVariant>): () => void;
|
|
204
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/order/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAgC,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAOvF,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,SAAS,GAAG,cAAc,CAAC;AAClE,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAE5C,+FAA+F;AAC/F,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,YAAY,GAAG,SAAS,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,QAAQ,CAAC;AAEjI,+DAA+D;AAC/D,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AAEpD,4DAA4D;AAC5D,MAAM,WAAW,cAAc;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,GAAG;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,sFAAsF;IACtF,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC3B;AAED,+FAA+F;AAC/F,MAAM,WAAW,UAAU;IACvB,0BAA0B;IAC1B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;IAC9B;;;OAGG;IACH,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,wBAAwB;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,mGAAmG;IACnG,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,GAAG;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,mFAAmF;IACnF,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnD,QAAQ,CAAC,EAAE;QACP,sEAAsE;QACtE,QAAQ,CAAC,EAAE,cAAc,GAAG;YAAE,IAAI,EAAE,cAAc,CAAC;YAAC,EAAE,CAAC,EAAE,cAAc,CAAA;SAAE,CAAC;QAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,mDAAmD;QACnD,WAAW,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,8DAA8D;IAC9D,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC,CAAC;IACzD,6BAA6B;IAC7B,OAAO,CAAC,EAAE;QACN,KAAK,CAAC,EAAE,cAAc,CAAC;QACvB,GAAG,CAAC,EAAE,cAAc,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,wCAAwC;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iGAAiG;IACjG,UAAU,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,+FAA+F;AAC/F,MAAM,WAAW,SAAS;IACtB,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,gDAAgD;AAChD,MAAM,WAAW,SAAS;IACtB,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE;QAAE,MAAM,EAAE,WAAW,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,8DAA8D;IAC9D,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE;QACP,QAAQ,CAAC,EAAE;YAAE,IAAI,EAAE,SAAS,CAAC;YAAC,EAAE,CAAC,EAAE,SAAS,CAAA;SAAE,GAAG;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC;QAClE,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE;QACN,KAAK,CAAC,EAAE,SAAS,CAAC;QAClB,GAAG,CAAC,EAAE,SAAS,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,GAAG,CAAC,EAAE,MAAM,CAAC;CAChB;AAiKD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAoB9D;AAySD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAiGxD;AAuED,qFAAqF;AACrF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,GAAG,MAAM,CAErD;AAkZD,eAAO,MAAM,SAAS;;;;;;;;;CAiBpB,CAAC;AAEH,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,OAAO,GAAE,gBAAgB,CAAC,SAAS,EAAE,YAAY,CAAM,GAAG,MAAM,IAAI,CAElG"}
|