@aparte/plugin-cards 0.0.0-stage → 0.29.0
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 +148 -2
- package/dist/carousel-CPy-0JGN.js +63 -0
- package/dist/carousel-CPy-0JGN.js.map +1 -0
- package/dist/controls-BVX2u7sK.js +62 -0
- package/dist/controls-BVX2u7sK.js.map +1 -0
- package/dist/format-CchcJRum.js +720 -0
- package/dist/format-CchcJRum.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -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 +194 -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/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-B8Mdx_b_.js +35 -0
- package/dist/rating-B8Mdx_b_.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
package/dist/plan.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.js","sources":["../src/plan/icons.ts","../src/plan/plan.css?raw","../src/plan/index.ts"],"sourcesContent":["/**\n * The plan card's glyphs: one per step status, the timeline's \"current\" node, a clock for\n * the elapsed time, and the card's own for its one-line states.\n *\n * Drawn on a 24 × 24 grid in `currentColor`, so the sheet colours each status through\n * `color` and a theme follows. The check and the cross inside a disc are drawn in the\n * card's surface (`.aparte-plan__mark`), so they read in light and dark alike. Constants\n * of this package — the only markup the card parses.\n */\n\nconst svg = (body: string, className?: string) =>\n `<svg${className ? ` class=\"${className}\"` : ''} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" xmlns=\"http://www.w3.org/2000/svg\">${body}</svg>`; // safe-attr: a class name this file passes, a constant, never data // safe-text: this package's own SVG, built from its glyph constants\n\n/** An empty ring: not started yet. */\nconst PENDING = svg('<circle cx=\"12\" cy=\"12\" r=\"8\"/>');\n\n/** A faint ring with a quarter of it drawn, which the sheet turns: running now. Still under reduced motion. */\nconst RUNNING = svg('<circle cx=\"12\" cy=\"12\" r=\"8\" opacity=\"0.4\"/><path d=\"M12 4a8 8 0 0 1 8 8\"/>', 'aparte-plan__spinner');\n\n/** A filled disc with a check: done. */\nconst DONE = svg('<circle cx=\"12\" cy=\"12\" r=\"9\" fill=\"currentColor\" stroke=\"none\"/><path class=\"aparte-plan__mark\" d=\"M8.2 12.4l2.6 2.6 5-5.4\"/>');\n\n/** A filled disc with a cross: failed. */\nconst FAILED = svg('<circle cx=\"12\" cy=\"12\" r=\"9\" fill=\"currentColor\" stroke=\"none\"/><path class=\"aparte-plan__mark\" d=\"M9.3 9.3l5.4 5.4M14.7 9.3l-5.4 5.4\"/>');\n\n/** A dashed ring crossed by a dash: skipped, left out on purpose. */\nconst SKIPPED = svg('<circle cx=\"12\" cy=\"12\" r=\"8\" stroke-dasharray=\"2.4 2.6\"/><path d=\"M8.6 12h6.8\"/>');\n\n/** A padlock: blocked, waiting on something outside the plan. */\nconst BLOCKED = svg('<rect x=\"6\" y=\"10.6\" width=\"12\" height=\"9\" rx=\"2.2\"/><path d=\"M8.9 10.6V8.2a3.1 3.1 0 0 1 6.2 0v2.4\"/>');\n\n/** The glyph each status wears in a list. */\nexport const STATUS_GLYPHS = {\n pending: PENDING,\n 'in-progress': RUNNING,\n done: DONE,\n failed: FAILED,\n skipped: SKIPPED,\n blocked: BLOCKED,\n} as const;\n\n/** The timeline's node for the step under way: a ring round a dot, which the sheet makes pulse. */\nexport const NODE_CURRENT = svg('<circle cx=\"12\" cy=\"12\" r=\"8\"/><circle cx=\"12\" cy=\"12\" r=\"4\" fill=\"currentColor\" stroke=\"none\"/>');\n\n/** A clock, beside the elapsed time. */\nexport const CLOCK = svg('<circle cx=\"12\" cy=\"12\" r=\"8.2\"/><path d=\"M12 7.6V12l2.9 1.9\"/>');\n\n/** A clipboard with two ticked lines: the card's own glyph, for its one-line states. */\nexport const PLAN = svg('<rect x=\"5\" y=\"4.5\" width=\"14\" height=\"16.5\" rx=\"2.2\"/><path d=\"M9.2 4.5V3.8a1 1 0 0 1 1-1h3.6a1 1 0 0 1 1 1v.7\"/><path d=\"M8.4 10.6l1.4 1.4 2.4-2.6\"/><path d=\"M14.2 10.6h1.8\"/><path d=\"M8.4 16.1l1.4 1.4 2.4-2.6\"/><path d=\"M14.2 16.1h1.8\"/>');\n","export default \"/* =========================================\\n The plan card — @aparte/plugin-cards/plan\\n =========================================\\n Three variants of one card, chosen by `data-variant` on its root:\\n checklist \\\"3 of 7 done\\\" over a thin bar, then a row per step: its status glyph,\\n its title, a detail line, its duration at the end once it finished.\\n timeline a rail with a node per step, coloured by its status, its start time and\\n duration under the title; the step under way pulses gently.\\n compact one line — \\\"Step 3 of 7: Running the tests\\\", a ring, the percentage.\\n An agent re-sends this card while it works, and the kit draws the new one into the\\n same root, so nothing here depends on the status for its SIZE: the glyph is one size\\n for all, the title reserves its bold width (and its other wording's), a timed plan\\n keeps every time slot, as wide as the language's widest duration.\\n A status is never said by colour alone: each has its own glyph shape, and its word for\\n a screen reader. Every colour is a theme token or derived from one.\\n */\\n\\n.aparte-plan-card {\\n --aparte-plan-pad: var(--aparte-space-6);\\n --aparte-plan-glyph-size: 1.125rem;\\n --aparte-plan-node-size: 1.25rem;\\n --aparte-plan-pending: color-mix(in oklab, var(--aparte-text-muted) 75%, transparent);\\n --aparte-plan-active: var(--aparte-primary);\\n --aparte-plan-done: var(--aparte-success);\\n --aparte-plan-failed: var(--aparte-error);\\n --aparte-plan-failed-ink: var(--aparte-error-ink);\\n --aparte-plan-skipped: var(--aparte-text-muted);\\n --aparte-plan-blocked: var(--aparte-warning);\\n --aparte-plan-blocked-ink: var(--aparte-warning-ink);\\n --aparte-plan-track: color-mix(in oklab, var(--aparte-text) 9%, transparent);\\n --aparte-plan-rail: color-mix(in oklab, var(--aparte-text) 13%, transparent);\\n --aparte-plan-active-tint: color-mix(in oklab, var(--aparte-primary) 8%, transparent);\\n --aparte-plan-hover-tint: color-mix(in oklab, var(--aparte-text) 3.5%, transparent);\\n --aparte-plan-done-text: var(--aparte-text-muted);\\n}\\n\\n.aparte-plan-card[data-variant=\\\"checklist\\\"],\\n.aparte-plan-card[data-variant=\\\"timeline\\\"] { --aparte-rich-card-max-inline-size: 26rem; }\\n.aparte-plan-card[data-variant=\\\"compact\\\"] { --aparte-rich-card-max-inline-size: 24rem; }\\n\\n/* ── Header: title, \\\"3 of 7 done\\\", the bar ───────────────────────────── */\\n\\n.aparte-plan__head {\\n display: flex;\\n flex-direction: column;\\n gap: var(--aparte-space-3);\\n padding: var(--aparte-plan-pad) var(--aparte-plan-pad) 0;\\n min-inline-size: 0;\\n}\\n\\n.aparte-plan__heading {\\n display: flex;\\n flex-direction: column;\\n gap: var(--aparte-space-1);\\n min-inline-size: 0;\\n}\\n\\n.aparte-plan__title {\\n color: var(--aparte-text);\\n font-size: var(--aparte-font-size-base);\\n font-weight: var(--aparte-font-weight-semibold);\\n line-height: 1.3;\\n overflow-wrap: anywhere;\\n}\\n\\n.aparte-plan__goal {\\n color: var(--aparte-text-muted);\\n font-size: var(--aparte-font-size-sm);\\n overflow-wrap: anywhere;\\n}\\n\\n/* One line, always: a count that grows a digit or a note that appears does not wrap the\\n header and push the steps down while the agent works. */\\n.aparte-plan__meter {\\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-sm);\\n font-variant-numeric: tabular-nums;\\n white-space: nowrap;\\n}\\n\\n.aparte-plan__count {\\n flex: none;\\n color: var(--aparte-text);\\n font-weight: var(--aparte-font-weight-medium);\\n}\\n\\n.aparte-plan__note {\\n min-inline-size: 0;\\n overflow: hidden;\\n text-overflow: ellipsis;\\n}\\n\\n.aparte-plan__note::before {\\n content: \\\"·\\\";\\n margin-inline-end: var(--aparte-space-3);\\n color: var(--aparte-text-muted);\\n}\\n\\n.aparte-plan__note[data-kind=\\\"failed\\\"] { color: var(--aparte-plan-failed-ink); }\\n\\n.aparte-plan__elapsed {\\n display: inline-flex;\\n flex: none;\\n align-items: center;\\n gap: var(--aparte-space-1);\\n margin-inline-start: auto;\\n --aparte-rich-card-glyph-size: 0.95rem;\\n}\\n\\n.aparte-plan__clock { opacity: 0.8; }\\n\\n/* The bar: what is done in the accent (green once all of it is), what was skipped after\\n it in hatching — counted, but plainly not the same as done. */\\n.aparte-plan__bar {\\n display: flex;\\n block-size: 0.375rem;\\n overflow: hidden;\\n border-radius: var(--aparte-radius-full);\\n background: var(--aparte-plan-track);\\n}\\n\\n.aparte-plan__bar-done,\\n.aparte-plan__bar-skipped {\\n flex: none;\\n block-size: 100%;\\n transition: inline-size var(--aparte-duration-slow, 400ms) var(--aparte-ease, ease);\\n}\\n\\n.aparte-plan__bar-done {\\n inline-size: var(--aparte-plan-done-share, 0%);\\n background: var(--aparte-plan-active);\\n}\\n\\n.aparte-plan__bar[data-complete] .aparte-plan__bar-done { background: var(--aparte-plan-done); }\\n\\n.aparte-plan__bar-skipped {\\n inline-size: var(--aparte-plan-skipped-share, 0%);\\n background: repeating-linear-gradient(\\n -45deg,\\n color-mix(in oklab, var(--aparte-plan-skipped) 55%, transparent) 0 2px,\\n color-mix(in oklab, var(--aparte-plan-skipped) 22%, transparent) 2px 4px\\n );\\n}\\n\\n/* ── A step's parts, whatever the variant ────────────────────────────── */\\n\\n/* The text and an invisible bold copy share one cell: the cell is always as wide and as\\n tall as the bold form, so the step that starts running reflows nothing. A todo's other\\n wording (`activeForm`) is a second invisible copy in the same cell, so the switch to\\n \\\"Updating the reference pages\\\" from \\\"Update\\\" reflows nothing either. */\\n.aparte-plan__step-title {\\n display: grid;\\n min-inline-size: 0;\\n overflow-wrap: anywhere;\\n}\\n\\n.aparte-plan__step-title > .aparte-plan__step-title-text,\\n.aparte-plan__step-title[data-alt]::before,\\n.aparte-plan__step-title::after { grid-area: 1 / 1; }\\n\\n.aparte-plan__step-title[data-alt]::before {\\n content: attr(data-alt);\\n content: attr(data-alt) / \\\"\\\";\\n}\\n\\n.aparte-plan__step-title[data-alt]::before,\\n.aparte-plan__step-title::after {\\n font-weight: var(--aparte-font-weight-semibold);\\n visibility: hidden;\\n pointer-events: none;\\n user-select: none;\\n}\\n\\n.aparte-plan__step-title::after {\\n content: attr(data-text);\\n content: attr(data-text) / \\\"\\\";\\n}\\n\\n.aparte-plan__detail {\\n display: -webkit-box;\\n margin-block-start: 0.125rem;\\n overflow: hidden;\\n color: var(--aparte-text-muted);\\n font-size: var(--aparte-font-size-sm);\\n line-height: 1.4;\\n overflow-wrap: anywhere;\\n -webkit-box-orient: vertical;\\n -webkit-line-clamp: 2;\\n line-clamp: 2;\\n}\\n\\n.aparte-plan__duration {\\n color: var(--aparte-text-muted);\\n font-size: var(--aparte-font-size-xs);\\n font-variant-numeric: tabular-nums;\\n white-space: nowrap;\\n}\\n\\n/* The status colours: the glyph takes them; the text only where it must speak. */\\n[data-status] > .aparte-plan__row > .aparte-plan__glyph,\\n[data-status] > .aparte-plan__node,\\n.aparte-plan__compact > .aparte-plan__glyph { color: var(--aparte-plan-pending); }\\n\\n[data-status=\\\"in-progress\\\"] > .aparte-plan__row > .aparte-plan__glyph,\\n[data-status=\\\"in-progress\\\"] > .aparte-plan__node,\\n.aparte-plan__compact[data-status=\\\"in-progress\\\"] > .aparte-plan__glyph { color: var(--aparte-plan-active); }\\n\\n[data-status=\\\"done\\\"] > .aparte-plan__row > .aparte-plan__glyph,\\n[data-status=\\\"done\\\"] > .aparte-plan__node,\\n.aparte-plan__compact[data-status=\\\"done\\\"] > .aparte-plan__glyph { color: var(--aparte-plan-done); }\\n\\n[data-status=\\\"failed\\\"] > .aparte-plan__row > .aparte-plan__glyph,\\n[data-status=\\\"failed\\\"] > .aparte-plan__node,\\n.aparte-plan__compact[data-status=\\\"failed\\\"] > .aparte-plan__glyph { color: var(--aparte-plan-failed); }\\n\\n[data-status=\\\"skipped\\\"] > .aparte-plan__row > .aparte-plan__glyph,\\n[data-status=\\\"skipped\\\"] > .aparte-plan__node { color: var(--aparte-plan-skipped); }\\n\\n[data-status=\\\"blocked\\\"] > .aparte-plan__row > .aparte-plan__glyph,\\n[data-status=\\\"blocked\\\"] > .aparte-plan__node,\\n.aparte-plan__compact[data-status=\\\"blocked\\\"] > .aparte-plan__glyph { color: var(--aparte-plan-blocked-ink); }\\n\\n/* The check and the cross are cut out of their disc in the card's own surface. */\\n.aparte-plan__mark {\\n stroke: var(--aparte-surface-1);\\n stroke-width: 2.2;\\n}\\n\\n/* The running ring turns, on core's own keyframe — its sheet comes with the chat; a copy\\n here was a second name for the same turn. Under reduced motion the kit stops it and the\\n arc stays. */\\n.aparte-plan__spinner {\\n transform-origin: 50% 50%;\\n animation: aparte-spin 0.9s linear infinite;\\n}\\n\\n/* Done stays readable — muted, not struck through. Failed speaks in the error ink. */\\n[data-status=\\\"done\\\"] > .aparte-plan__row .aparte-plan__step-title,\\n[data-status=\\\"done\\\"] > .aparte-plan__event-body > .aparte-plan__step-title,\\n[data-status=\\\"skipped\\\"] > .aparte-plan__row .aparte-plan__step-title,\\n[data-status=\\\"skipped\\\"] > .aparte-plan__event-body > .aparte-plan__step-title { color: var(--aparte-plan-done-text); }\\n\\n[data-status=\\\"failed\\\"] > .aparte-plan__row .aparte-plan__step-title,\\n[data-status=\\\"failed\\\"] > .aparte-plan__event-body > .aparte-plan__step-title { color: var(--aparte-plan-failed-ink); }\\n\\n[data-status=\\\"blocked\\\"] > .aparte-plan__row .aparte-plan__step-title,\\n[data-status=\\\"blocked\\\"] > .aparte-plan__event-body > .aparte-plan__step-title { color: var(--aparte-text-muted); }\\n\\n[data-status=\\\"in-progress\\\"] > .aparte-plan__row .aparte-plan__step-title,\\n[data-status=\\\"in-progress\\\"] > .aparte-plan__event-body > .aparte-plan__step-title {\\n color: var(--aparte-text);\\n font-weight: var(--aparte-font-weight-semibold);\\n}\\n\\n/* ── checklist ───────────────────────────────────────────────────────── */\\n\\n.aparte-plan__steps,\\n.aparte-plan__substeps,\\n.aparte-plan__timeline {\\n margin: 0;\\n padding: 0;\\n list-style: none;\\n}\\n\\n.aparte-plan__steps {\\n display: flex;\\n flex-direction: column;\\n gap: 0.125rem;\\n padding: var(--aparte-space-4) calc(var(--aparte-plan-pad) - var(--aparte-space-3)) calc(var(--aparte-plan-pad) - var(--aparte-space-2));\\n}\\n\\n.aparte-plan__row {\\n display: grid;\\n grid-template-columns: var(--aparte-plan-glyph-size) minmax(0, 1fr) auto;\\n column-gap: var(--aparte-space-3);\\n align-items: baseline;\\n padding: var(--aparte-space-2) var(--aparte-space-3);\\n border-radius: var(--aparte-radius-md);\\n color: var(--aparte-text);\\n font-size: var(--aparte-font-size-md);\\n line-height: 1.4;\\n transition: background-color var(--aparte-duration-fast, 120ms) ease;\\n}\\n\\n.aparte-plan__row:hover { background: var(--aparte-plan-hover-tint); }\\n\\n/* The step under way: a soft tint of the accent, held on hover. */\\n.aparte-plan__step[data-status=\\\"in-progress\\\"] > .aparte-plan__row,\\n.aparte-plan__step[data-status=\\\"in-progress\\\"] > .aparte-plan__row:hover { background: var(--aparte-plan-active-tint); }\\n\\n.aparte-plan__step > .aparte-plan__row > .aparte-plan__glyph {\\n --aparte-rich-card-glyph-size: var(--aparte-plan-glyph-size);\\n align-self: start;\\n /* Centred on the first line of the title, however the font sizes it. */\\n margin-block-start: calc((1lh - var(--aparte-plan-glyph-size)) / 2);\\n}\\n\\n.aparte-plan__text { min-inline-size: 0; }\\n\\n/* A timed plan keeps the duration's column on every row, filled or not, as wide as the\\n language's widest duration: an invisible copy of it (`data-reserve`, written by the card)\\n lies under the text with no height, so `1 Std., 5 Min.` fills the slot without widening it\\n — a wider slot would narrow the title and could wrap it onto a second line. */\\n.aparte-plan__steps[data-timed] .aparte-plan__duration {\\n display: inline-flex;\\n flex-direction: column;\\n align-items: flex-end;\\n}\\n\\n.aparte-plan__steps[data-timed] .aparte-plan__duration::after {\\n content: attr(data-reserve);\\n content: attr(data-reserve) / \\\"\\\";\\n block-size: 0;\\n overflow: hidden;\\n visibility: hidden;\\n}\\n\\n.aparte-plan__substeps {\\n display: flex;\\n flex-direction: column;\\n gap: 0;\\n margin-inline-start: calc(var(--aparte-plan-glyph-size) + var(--aparte-space-3));\\n}\\n\\n.aparte-plan__step--sub {\\n --aparte-plan-glyph-size: 0.9375rem;\\n}\\n\\n.aparte-plan__step--sub > .aparte-plan__row {\\n padding-block: var(--aparte-space-1);\\n font-size: var(--aparte-font-size-sm);\\n}\\n\\n/* A sub-step under way is tinted by its parent's row already: it keeps only its weight. */\\n.aparte-plan__step--sub[data-status=\\\"in-progress\\\"] > .aparte-plan__row,\\n.aparte-plan__step--sub[data-status=\\\"in-progress\\\"] > .aparte-plan__row:hover { background: transparent; }\\n\\n/* ── timeline ────────────────────────────────────────────────────────── */\\n\\n.aparte-plan__timeline {\\n padding: var(--aparte-space-5) var(--aparte-plan-pad) var(--aparte-plan-pad);\\n}\\n\\n.aparte-plan__event {\\n position: relative;\\n display: grid;\\n grid-template-columns: var(--aparte-plan-node-size) minmax(0, 1fr);\\n column-gap: var(--aparte-space-4);\\n padding-block-end: var(--aparte-space-5);\\n color: var(--aparte-text);\\n font-size: var(--aparte-font-size-md);\\n line-height: 1.4;\\n}\\n\\n.aparte-plan__event:last-child { padding-block-end: 0; }\\n\\n/* The rail: from under this node to the top of the next one. Past a finished step it\\n takes the done colour, so the run reads as a line filling downwards. */\\n.aparte-plan__event:not(:last-child)::before {\\n content: \\\"\\\";\\n position: absolute;\\n inset-inline-start: calc(var(--aparte-plan-node-size) / 2 - 1px);\\n inset-block-start: calc((1lh - var(--aparte-plan-node-size)) / 2 + var(--aparte-plan-node-size) + 3px);\\n inset-block-end: calc((1lh - var(--aparte-plan-node-size)) / -2 + 3px);\\n inline-size: 2px;\\n border-radius: var(--aparte-radius-full);\\n background: var(--aparte-plan-rail);\\n}\\n\\n.aparte-plan__event[data-status=\\\"done\\\"]:not(:last-child)::before,\\n.aparte-plan__event[data-status=\\\"skipped\\\"]:not(:last-child)::before {\\n background: color-mix(in oklab, var(--aparte-plan-done) 55%, transparent);\\n}\\n\\n.aparte-plan__node {\\n position: relative;\\n display: grid;\\n place-items: center;\\n inline-size: var(--aparte-plan-node-size);\\n block-size: var(--aparte-plan-node-size);\\n margin-block-start: calc((1lh - var(--aparte-plan-node-size)) / 2);\\n border-radius: var(--aparte-radius-full);\\n --aparte-rich-card-glyph-size: var(--aparte-plan-node-size);\\n}\\n\\n/* The step under way breathes: a halo of the accent that widens and fades. */\\n.aparte-plan__event[data-status=\\\"in-progress\\\"] > .aparte-plan__node::after {\\n content: \\\"\\\";\\n position: absolute;\\n inset: 0;\\n border-radius: inherit;\\n animation: aparte-plan-pulse 2.2s cubic-bezier(0.2, 0.6, 0.35, 1) infinite;\\n}\\n\\n@keyframes aparte-plan-pulse {\\n 0% { box-shadow: 0 0 0 0 color-mix(in oklab, var(--aparte-plan-active) 45%, transparent); }\\n 70%, 100% { box-shadow: 0 0 0 0.45rem color-mix(in oklab, var(--aparte-plan-active) 0%, transparent); }\\n}\\n\\n.aparte-plan__event-body {\\n display: flex;\\n flex-direction: column;\\n min-inline-size: 0;\\n}\\n\\n/* Start and duration on one line under the title. In a timed plan the line is there for\\n every step, empty until it starts: every step keeps one rhythm. */\\n.aparte-plan__meta {\\n display: flex;\\n flex-wrap: wrap;\\n align-items: baseline;\\n gap: 0 var(--aparte-space-2);\\n min-block-size: 1lh;\\n color: var(--aparte-text-muted);\\n font-size: var(--aparte-font-size-xs);\\n font-variant-numeric: tabular-nums;\\n}\\n\\n.aparte-plan__meta > .aparte-plan__start:not(:empty) + .aparte-plan__duration:not(:empty)::before {\\n content: \\\"·\\\";\\n margin-inline-end: var(--aparte-space-2);\\n}\\n\\n.aparte-plan__event-body > .aparte-plan__substeps {\\n margin-block-start: var(--aparte-space-2);\\n margin-inline: calc(var(--aparte-space-3) * -1) 0;\\n}\\n\\n/* ── compact ─────────────────────────────────────────────────────────── */\\n\\n.aparte-plan__compact {\\n display: grid;\\n grid-template-columns: auto minmax(0, 1fr) auto auto;\\n align-items: center;\\n column-gap: var(--aparte-space-3);\\n padding: var(--aparte-space-3) var(--aparte-space-5);\\n font-size: var(--aparte-font-size-md);\\n --aparte-rich-card-glyph-size: 1.125rem;\\n}\\n\\n.aparte-plan__compact-text {\\n min-inline-size: 0;\\n overflow: hidden;\\n text-overflow: ellipsis;\\n white-space: nowrap;\\n}\\n\\n.aparte-plan__compact-step {\\n margin-inline-end: 0.35em;\\n color: var(--aparte-text-muted);\\n font-variant-numeric: tabular-nums;\\n}\\n\\n.aparte-plan__compact-title {\\n color: var(--aparte-text);\\n font-weight: var(--aparte-font-weight-semibold);\\n}\\n\\n/* Finished: the plan's name, then \\\"All steps done\\\". */\\n.aparte-plan__compact-title + .aparte-plan__compact-step { margin-inline: 0; }\\n.aparte-plan__compact-title + .aparte-plan__compact-step::before {\\n content: \\\"·\\\";\\n margin-inline: 0.4em;\\n}\\n\\n.aparte-plan__ring {\\n display: inline-flex;\\n inline-size: 1.375rem;\\n block-size: 1.375rem;\\n}\\n\\n/* The arc starts at twelve o'clock and runs with the reading direction. */\\n.aparte-plan__ring-svg {\\n inline-size: 100%;\\n block-size: 100%;\\n transform: rotate(-90deg);\\n}\\n\\n.aparte-plan__ring-svg:dir(rtl) { transform: scaleX(-1) rotate(-90deg); }\\n\\n.aparte-plan__ring-svg > circle {\\n fill: none;\\n stroke-width: 3;\\n}\\n\\n.aparte-plan__ring-track { stroke: var(--aparte-plan-track); }\\n.aparte-plan__ring-done { stroke: var(--aparte-plan-active); }\\n.aparte-plan__ring[data-complete] .aparte-plan__ring-done { stroke: var(--aparte-plan-done); }\\n.aparte-plan__ring-skipped { stroke: color-mix(in oklab, var(--aparte-plan-skipped) 45%, transparent); }\\n\\n.aparte-plan__percent {\\n min-inline-size: 4ch;\\n color: var(--aparte-text);\\n font-size: var(--aparte-font-size-sm);\\n font-weight: var(--aparte-font-weight-semibold);\\n font-variant-numeric: tabular-nums;\\n text-align: end;\\n}\\n\\n/* ── Loading ─────────────────────────────────────────────────────────── */\\n\\n.aparte-plan-card[data-state=\\\"loading\\\"] .aparte-skeleton.aparte-plan__sk-title { inline-size: 10rem; }\\n.aparte-plan-card[data-state=\\\"loading\\\"] .aparte-skeleton.aparte-plan__sk-count { inline-size: 6rem; }\\n\\n.aparte-plan-card[data-state=\\\"loading\\\"] .aparte-skeleton.aparte-plan__sk-glyph {\\n inline-size: var(--aparte-plan-glyph-size);\\n block-size: var(--aparte-plan-glyph-size);\\n align-self: center;\\n margin: 0;\\n}\\n\\n.aparte-plan-card[data-state=\\\"loading\\\"] .aparte-skeleton.aparte-plan__sk-node {\\n inline-size: var(--aparte-plan-node-size);\\n block-size: var(--aparte-plan-node-size);\\n}\\n\\n.aparte-plan-card[data-state=\\\"loading\\\"] .aparte-skeleton.aparte-plan__sk-ring {\\n inline-size: 1.375rem;\\n block-size: 1.375rem;\\n}\\n\\n.aparte-plan-card[data-state=\\\"loading\\\"] .aparte-plan__row { align-items: center; }\\n.aparte-plan-card[data-state=\\\"loading\\\"] .aparte-skeleton.aparte-plan__sk-line { inline-size: 70%; }\\n.aparte-plan-card[data-state=\\\"loading\\\"] li:nth-child(2) .aparte-skeleton.aparte-plan__sk-line { inline-size: 55%; }\\n.aparte-plan-card[data-state=\\\"loading\\\"] li:nth-child(3) .aparte-skeleton.aparte-plan__sk-line { inline-size: 80%; }\\n.aparte-plan-card[data-state=\\\"loading\\\"] li:nth-child(4) .aparte-skeleton.aparte-plan__sk-line { inline-size: 45%; }\\n.aparte-plan-card[data-state=\\\"loading\\\"] .aparte-skeleton.aparte-plan__sk-meta { inline-size: 5rem; }\\n.aparte-plan-card[data-state=\\\"loading\\\"] .aparte-plan__compact .aparte-skeleton.aparte-plan__sk-line { inline-size: 75%; }\\n\\n/* ── Small screens ───────────────────────────────────────────────────── */\\n\\n@media (max-width: 380px) {\\n .aparte-plan-card { --aparte-plan-pad: var(--aparte-space-5); }\\n .aparte-plan__compact { column-gap: var(--aparte-space-2); padding-inline: var(--aparte-space-4); }\\n}\\n\"","/**\n * @aparte/plugin-cards/plan — an agent's plan, its task list, or its progress through steps.\n *\n * import { setupPlanCard } from '@aparte/plugin-cards/plan';\n *\n * setupPlanCard({ tool: 'update_plan' });\n *\n * // A tool whose INPUT is the list and whose result is only \"ok\" — Claude Code's\n * // `TodoWrite`, say — reads its input instead:\n * setupPlanCard({ tool: 'TodoWrite', map: (_result, input) => input });\n *\n * Three variants of one card: `checklist` (a header with \"3 of 7 done\" and a thin bar,\n * then one row per step with its status glyph, its detail and, once it finished, its\n * duration), `timeline` (a rail with a node per step coloured by its status, the start\n * time and duration beside each, the current node pulsing) and `compact` (one line —\n * \"Step 3 of 7: Running the tests\", a ring and the percentage — for a running plan in a\n * small space).\n *\n * This is the card an agent updates while it works: the same call re-sent with new\n * statuses, which the kit draws into the SAME root. So a step changing STATUS changes no\n * row's height: the title holds the room of its bold form and, for a todo with an active\n * form (Claude Code's `activeForm`), of both wordings; a timed plan keeps every row's\n * duration slot, filled or not, as wide as the language's widest duration; and the\n * timeline keeps every node's time line.\n *\n * What the card cannot hold for an update it has not seen: a detail the update brings (a\n * line under the title appears), a title the tool rewrites, and the first time a plan\n * carries — every row gains its time slot then, unless the plan said `timed: true` from its\n * first draw. LangGraph's `past_steps` results are not drawn at all: every step gains one\n * on completing, so every step would grow; a `map` that wants them gives them as `detail`.\n *\n * Your tool keeps its own fields: `readPlan` reads `{ title, goal, steps }`, a bare list of\n * steps, a list of strings (each a step to do), a Markdown checklist (`- [x] Write the\n * tests`), and the shapes agents already emit — Claude Code's and OpenAI's todo lists\n * (`todos[]` of `content`/`activeForm`/`status`), Codex's `plan[]` of `step`/`status`,\n * LangGraph's plan-and-execute state (`plan: string[]` and `past_steps` of\n * `[step, result]`), and `tasks[]` of `name`/`state`/`done`. `map` covers the rest.\n */\nimport type { AparteConfig } from '@aparte/core';\nimport { defineCard, type CardContext, type CardSetupOptions } from '../kit/define.js';\nimport { el, glyph, num, str } from '../kit/dom.js';\nimport { formatNumber, formatPercent, toDate } from '../kit/format.js';\nimport { intlTag, word, type CardWord } from '../kit/strings.js';\nimport { CLOCK, NODE_CURRENT, PLAN, STATUS_GLYPHS } from './icons.js';\nimport planStyles from './plan.css?raw';\n\nexport type PlanVariant = 'checklist' | 'timeline' | 'compact';\n\n/** Where a step stands. */\nexport type PlanStatus = 'pending' | 'in-progress' | 'done' | 'failed' | 'skipped' | 'blocked';\n\ntype When = string | number | Date;\n\n/**\n * One step as `map` returns it. Only `title` is needed; a step without `status` is\n * `pending` (or, when it has sub-steps, where they stand).\n */\nexport interface PlanStepInput {\n title: string;\n /** The wording while it runs (\"Running the tests\"), Claude Code's `activeForm`: shown while the step is in progress. */\n activeForm?: string;\n /** `pending`, `in-progress`, `done`, `failed`, `skipped`, `blocked` — or the usual words for them (`todo`, `running`, `completed`, `error`, `cancelled`…). */\n status?: PlanStatus | string;\n /** One or two lines under the title: what it did, why it failed. */\n detail?: string;\n startedAt?: When;\n finishedAt?: When;\n /** In seconds; worked out from the two dates when it is absent. */\n duration?: number;\n /** One level of sub-steps. */\n substeps?: Array<PlanStepInput | string>;\n id?: string;\n}\n\n/** What `map` returns: a plan, a list of steps, or a list of strings. */\nexport type PlanInput =\n | {\n title?: string;\n goal?: string;\n steps: Array<PlanStepInput | string>;\n updatedAt?: When;\n /**\n * The steps will carry times (a start, a duration) — say it before the first one does,\n * and every row keeps its time slot from the first draw, so the update that brings the\n * first time moves no row. Without it, the slots appear with that update.\n */\n timed?: boolean;\n }\n | Array<PlanStepInput | string>;\n\n/** One step as the card draws it, once read. */\nexport interface PlanStep {\n /** The wording shown now: the active form while the step runs, when it has one. */\n title: string;\n /**\n * The other wording — the active form while `title` is the plain one, and the reverse.\n * Never shown: the title holds its room too, so the update that switches wording moves no row.\n */\n altTitle?: string;\n status: PlanStatus;\n detail?: string;\n startedAt?: Date;\n finishedAt?: Date;\n /** Seconds, never negative. */\n duration?: number;\n /** Never nested deeper. */\n substeps?: PlanStep[];\n id?: string;\n}\n\n/** The data as the card draws it. */\nexport interface PlanData {\n title?: string;\n goal?: string;\n /** One at least; at most fifty, sub-steps counted. */\n steps: PlanStep[];\n updatedAt?: Date;\n /** The plan said its steps will carry times: their slots are kept from the first draw. */\n timed?: boolean;\n}\n\n/** How far a plan has got, over its top-level steps. */\nexport interface PlanProgress {\n total: number;\n done: number;\n skipped: number;\n failed: number;\n blocked: number;\n /** `done` + `skipped`: what the bar fills with. */\n finished: number;\n /** `finished / total`, 0–1. */\n ratio: number;\n /** The step under way: the first in progress, else the first to do, else the first blocked. */\n current?: PlanStep;\n currentIndex?: number;\n /** With nothing left to do, the first step that failed — where the plan stopped. */\n stoppedIndex?: number;\n /** Seconds from the first start to the last date the plan holds, else the sum of the durations. */\n elapsed?: number;\n}\n\nconst W = {\n cardsPlanTitle: { en: 'Plan', fr: 'Plan', de: 'Plan', es: 'Plan', it: 'Piano', ja: '計画', ko: '계획', pt: 'Plano', ru: 'План', zh: '计划' },\n cardsPlanProgress: {\n en: '{done} of {total} done', fr: '{done} sur {total} terminées', de: '{done} von {total} erledigt', es: '{done} de {total} completados',\n it: '{done} di {total} completati', ja: '{total}件中{done}件完了', ko: '{total}개 중 {done}개 완료', pt: '{done} de {total} concluídos',\n ru: 'Выполнено {done} из {total}', zh: '已完成 {done}/{total}',\n },\n cardsPlanSkipped: {\n en: '{n} skipped', fr: 'Ignorées : {n}', de: 'Übersprungen: {n}', es: 'Omitidos: {n}', it: 'Saltati: {n}',\n ja: 'スキップ {n}件', ko: '건너뜀 {n}개', pt: 'Ignorados: {n}', ru: 'Пропущено: {n}', zh: '已跳过 {n} 项',\n },\n cardsPlanFailed: {\n en: '{n} failed', fr: 'Échecs : {n}', de: 'Fehlgeschlagen: {n}', es: 'Fallidos: {n}', it: 'Falliti: {n}',\n ja: '失敗 {n}件', ko: '실패 {n}개', pt: 'Falhas: {n}', ru: 'Ошибок: {n}', zh: '失败 {n} 项',\n },\n cardsPlanElapsed: {\n en: '{time} elapsed', fr: 'Durée : {time}', de: 'Dauer: {time}', es: 'Duración: {time}', it: 'Durata: {time}',\n ja: '経過時間 {time}', ko: '경과 {time}', pt: 'Duração: {time}', ru: 'Прошло: {time}', zh: '用时 {time}',\n },\n cardsPlanStepNow: {\n en: 'Step {n} of {total}:', fr: 'Étape {n} sur {total} :', de: 'Schritt {n} von {total}:', es: 'Paso {n} de {total}:',\n it: 'Passo {n} di {total}:', ja: 'ステップ {n}/{total}:', ko: '{total}단계 중 {n}단계:', pt: 'Etapa {n} de {total}:',\n ru: 'Шаг {n} из {total}:', zh: '第 {n}/{total} 步:',\n },\n cardsPlanAllDone: {\n en: 'All steps done', fr: 'Toutes les étapes sont terminées', de: 'Alle Schritte erledigt', es: 'Todos los pasos completados',\n it: 'Tutti i passi completati', ja: 'すべてのステップが完了', ko: '모든 단계 완료', pt: 'Todas as etapas concluídas',\n ru: 'Все шаги выполнены', zh: '所有步骤已完成',\n },\n cardsPlanStatusPending: { en: 'To do', fr: 'À faire', de: 'Offen', es: 'Pendiente', it: 'Da fare', ja: '未着手', ko: '대기', pt: 'A fazer', ru: 'Не начато', zh: '待办' },\n cardsPlanStatusInProgress: { en: 'In progress', fr: 'En cours', de: 'In Arbeit', es: 'En curso', it: 'In corso', ja: '進行中', ko: '진행 중', pt: 'Em andamento', ru: 'Выполняется', zh: '进行中' },\n cardsPlanStatusDone: { en: 'Done', fr: 'Terminé', de: 'Erledigt', es: 'Hecho', it: 'Fatto', ja: '完了', ko: '완료', pt: 'Concluído', ru: 'Готово', zh: '已完成' },\n cardsPlanStatusFailed: { en: 'Failed', fr: 'Échec', de: 'Fehlgeschlagen', es: 'Fallido', it: 'Non riuscito', ja: '失敗', ko: '실패', pt: 'Falhou', ru: 'Ошибка', zh: '失败' },\n cardsPlanStatusSkipped: { en: 'Skipped', fr: 'Ignoré', de: 'Übersprungen', es: 'Omitido', it: 'Saltato', ja: 'スキップ', ko: '건너뜀', pt: 'Ignorado', ru: 'Пропущено', zh: '已跳过' },\n cardsPlanStatusBlocked: { en: 'Blocked', fr: 'Bloqué', de: 'Blockiert', es: 'Bloqueado', it: 'Bloccato', ja: 'ブロック中', ko: '차단됨', pt: 'Bloqueado', ru: 'Заблокировано', zh: '受阻' },\n cardsPlanSummary: {\n en: '{done}/{total} done', fr: '{done}/{total} terminées', de: '{done}/{total} erledigt', es: '{done}/{total} completados',\n it: '{done}/{total} completati', ja: '{done}/{total} 完了', ko: '{done}/{total} 완료', pt: '{done}/{total} concluídos',\n ru: 'выполнено {done}/{total}', zh: '已完成 {done}/{total}',\n },\n cardsPlanNow: {\n en: 'now: {step}', fr: 'en cours : {step}', de: 'jetzt: {step}', es: 'ahora: {step}', it: 'ora: {step}',\n ja: '実行中:{step}', ko: '현재: {step}', pt: 'agora: {step}', ru: 'сейчас: {step}', zh: '当前:{step}',\n },\n} satisfies Record<string, CardWord>;\n\ntype WordKey = keyof typeof W;\n\nconst say = (config: AparteConfig, key: WordKey, vars?: Record<string, string>) => word(config, key, W[key], vars);\n\nconst STATUS_WORD: Record<PlanStatus, WordKey> = {\n pending: 'cardsPlanStatusPending',\n 'in-progress': 'cardsPlanStatusInProgress',\n done: 'cardsPlanStatusDone',\n failed: 'cardsPlanStatusFailed',\n skipped: 'cardsPlanStatusSkipped',\n blocked: 'cardsPlanStatusBlocked',\n};\n\n/** Fifty, sub-steps counted: past that a plan is a backlog, not something to follow. */\nexport const PLAN_STEPS_SHOWN = 50;\n\n// ── Reading ──────────────────────────────────────────────────────────────────\n\ntype Raw = Record<string, unknown>;\n\nconst isObject = (value: unknown): value is Raw => !!value && typeof value === 'object' && !Array.isArray(value);\n\nconst notNull = <T>(value: T | null | undefined): value is T => value !== null && value !== undefined;\n\n/** The first of `keys` that `r` has a value for. */\nfunction pick(r: Raw, keys: readonly string[]): unknown {\n for (const key of keys) if (r[key] !== undefined && r[key] !== null && r[key] !== '') return r[key];\n return undefined;\n}\n\n/** The words tools use for each status, written lower-case with dashes. */\nconst STATUS_ALIASES: Record<PlanStatus, readonly string[]> = {\n pending: ['pending', 'todo', 'to-do', 'open', 'queued', 'planned', 'not-started', 'notstarted', 'new', 'waiting', 'scheduled', 'upcoming', 'ready', 'idle', 'backlog'],\n 'in-progress': ['in-progress', 'inprogress', 'progress', 'doing', 'running', 'active', 'current', 'started', 'working', 'executing', 'ongoing', 'processing', 'in-flight'],\n done: ['done', 'completed', 'complete', 'success', 'succeeded', 'successful', 'finished', 'ok', 'passed', 'resolved', 'closed', 'checked'],\n failed: ['failed', 'fail', 'failure', 'error', 'errored', 'aborted', 'timeout', 'timed-out', 'crashed'],\n skipped: ['skipped', 'skip', 'cancelled', 'canceled', 'omitted', 'not-needed', 'wont-do', \"won't-do\", 'wontdo', 'obsolete', 'dropped'],\n blocked: ['blocked', 'on-hold', 'onhold', 'paused', 'stuck', 'held', 'needs-input', 'needs-approval', 'awaiting-input', 'awaiting-approval'],\n};\n\nconst STATUS_OF = new Map<string, PlanStatus>(\n (Object.entries(STATUS_ALIASES) as Array<[PlanStatus, readonly string[]]>).flatMap(([status, words]) => words.map((w) => [w, status] as const)),\n);\n\n/** A status from what a tool wrote — `\"in_progress\"`, `\"Running\"`, `true` — or `undefined` for a word it does not know. */\nexport function readStatus(value: unknown): PlanStatus | undefined {\n if (typeof value === 'boolean') return value ? 'done' : 'pending';\n if (typeof value !== 'string') return undefined;\n return STATUS_OF.get(value.trim().toLowerCase().replace(/[\\s_]+/g, '-'));\n}\n\nconst STATUS_KEYS = ['status', 'state', 'stage', 'phase'] as const;\nconst DONE_FLAGS = ['done', 'completed', 'complete', 'checked', 'finished', 'isDone', 'is_done', 'isCompleted', 'is_completed'] as const;\nconst ACTIVE_FLAGS = ['active', 'running', 'current', 'inProgress', 'in_progress'] as const;\nconst TITLE_KEYS = ['title', 'content', 'name', 'label', 'text', 'task', 'step', 'action', 'description'] as const;\nconst ACTIVE_FORM_KEYS = ['activeForm', 'active_form', 'activeTitle', 'active_title'] as const;\nconst DETAIL_KEYS = ['detail', 'details', 'description', 'note', 'notes', 'summary', 'result', 'output', 'observation', 'message', 'reason'] as const;\nconst START_KEYS = ['startedAt', 'started_at', 'startTime', 'start_time', 'startDate', 'start_date', 'start', 'begin'] as const;\nconst END_KEYS = ['finishedAt', 'finished_at', 'completedAt', 'completed_at', 'endedAt', 'ended_at', 'endTime', 'end_time', 'doneAt', 'done_at', 'end'] as const;\nconst SECONDS_KEYS = ['duration', 'durationSeconds', 'duration_seconds', 'durationSec', 'elapsed', 'seconds', 'took'] as const;\nconst MS_KEYS = ['durationMs', 'duration_ms', 'elapsedMs', 'elapsed_ms', 'tookMs', 'took_ms'] as const;\nconst SUB_KEYS = ['substeps', 'subSteps', 'sub_steps', 'subtasks', 'subTasks', 'children', 'steps', 'todos', 'items', 'checklist'] as const;\nconst LIST_KEYS = ['steps', 'todos', 'tasks', 'items', 'checklist', 'stages', 'phases', 'subtasks', 'plan'] as const;\nconst PAST_KEYS = ['past_steps', 'pastSteps', 'completed_steps', 'completedSteps'] as const;\nconst PLAN_TITLE_KEYS = ['title', 'name', 'heading'] as const;\nconst GOAL_KEYS = ['goal', 'objective', 'task', 'input', 'query', 'request', 'description', 'explanation', 'summary'] as const;\nconst UPDATED_KEYS = ['updatedAt', 'updated_at', 'lastUpdated', 'last_updated', 'asOf', 'as_of', 'timestamp'] as const;\n\n/** A status the object says, by a word or by a flag. */\nfunction statusOf(r: Raw): PlanStatus | undefined {\n const said = readStatus(pick(r, STATUS_KEYS));\n if (said) return said;\n if (r['failed'] === true || r['error'] === true || str(r['error'])) return 'failed';\n if (r['skipped'] === true || r['cancelled'] === true || r['canceled'] === true) return 'skipped';\n if (r['blocked'] === true) return 'blocked';\n const active = ACTIVE_FLAGS.some((key) => r[key] === true);\n for (const key of DONE_FLAGS) {\n if (typeof r[key] === 'boolean') return r[key] ? 'done' : active ? 'in-progress' : 'pending';\n }\n return active ? 'in-progress' : undefined;\n}\n\n/**\n * A parent's status from its sub-steps, when it says none: done once they all are,\n * under way as soon as one started, failed when one failed and nothing else runs.\n */\nfunction fromSubsteps(subs: PlanStep[]): PlanStatus {\n const has = (status: PlanStatus) => subs.some((s) => s.status === status);\n if (subs.every((s) => s.status === 'done' || s.status === 'skipped')) return has('done') ? 'done' : 'skipped';\n if (has('in-progress')) return 'in-progress';\n if (has('failed')) return 'failed';\n if (has('done') || has('skipped')) return 'in-progress';\n if (has('blocked')) return 'blocked';\n return 'pending';\n}\n\n/** `- [x] Write the tests` → its title and status; a list marker and a checkbox are both optional. */\nconst LINE = /^\\s*(?:(?:[-*+•]|\\d+[.)])\\s+)?(?:\\[([ xX✓✔>~!-])\\]\\s*)?(.*?)\\s*$/;\n/** A line that is a list item: a marker, or a checkbox. */\nconst ITEM = /^\\s*(?:(?:[-*+•]|\\d+[.)])\\s+|\\[[ xX✓✔>~!-]\\])/;\n\nconst BOX: Record<string, PlanStatus> = { ' ': 'pending', x: 'done', X: 'done', '✓': 'done', '✔': 'done', '>': 'in-progress', '!': 'failed', '-': 'skipped', '~': 'blocked' };\n\nfunction checkbox(text: string): { title: string; status?: PlanStatus } | null {\n const match = LINE.exec(text)!;\n const title = match[2]!;\n if (!title) return null;\n return { title, ...(match[1] !== undefined ? { status: BOX[match[1]]! } : {}) };\n}\n\n/**\n * A Markdown checklist or list, as a model writes it in text: one step per item, an item\n * indented under another is its sub-step, a first line that is no item is the title.\n * `null` when a line further down is not an item — that is prose, not a plan.\n */\nfunction parseText(text: string): { title?: string; steps: PlanStep[] } | null {\n const lines = text.split(/\\r?\\n/).filter((line) => line.trim());\n let title: string | undefined;\n const steps: PlanStep[] = [];\n for (const [index, line] of lines.entries()) {\n if (!ITEM.test(line)) {\n if (index > 0) return null;\n // A model titles its list with a heading as often as with a sentence: `## Deploy plan`.\n title = line.trim().replace(/^#{1,6}\\s+/, '').replace(/:$/, '') || undefined;\n continue;\n }\n const step = readStep(line, 1);\n if (!step) continue;\n const parent = steps[steps.length - 1];\n // Indented by two spaces or more, or by a tab — one character, a whole level.\n if (/^(?:\\t|\\s{2,})/.test(line) && parent) (parent.substeps ??= []).push(step);\n else steps.push(step);\n }\n return steps.length ? { ...(title ? { title } : {}), steps } : null;\n}\n\n/** One step from whatever a tool wrote, or `null` without a title. `depth` 1 reads no sub-steps. */\nfunction readStep(raw: unknown, depth: number, forced?: PlanStatus): PlanStep | null {\n if (typeof raw === 'string' || typeof raw === 'number') {\n const line = checkbox(String(raw));\n return line ? { title: line.title, status: line.status ?? forced ?? 'pending' } : null;\n }\n if (Array.isArray(raw)) {\n // A `[step, result]` pair, as LangGraph's `past_steps` keeps them. The result is not\n // drawn: it arrives as the step completes, so as a detail it would add a line to every\n // step at that moment — and it is the executor's whole answer, not a line about the step.\n return readStep(raw[0], depth, forced);\n }\n if (!isObject(raw)) return null;\n const r = raw;\n // Only a text names a step: `step: 3` is an index, not a title.\n const titleKey = TITLE_KEYS.find((key) => typeof r[key] === 'string' && (r[key] as string).trim());\n let title = titleKey ? (r[titleKey] as string).trim() : undefined;\n if (!title) return null;\n\n const subsRaw = depth === 0 ? SUB_KEYS.map((key) => r[key]).find(Array.isArray) : undefined;\n const substeps = subsRaw ? subsRaw.map((item) => readStep(item, 1)).filter(notNull) : [];\n\n const startedAt = toDate(pick(r, START_KEYS)) ?? undefined;\n const finishedAt = toDate(pick(r, END_KEYS)) ?? undefined;\n const status = statusOf(r) ?? forced\n ?? (substeps.length ? fromSubsteps(substeps) : finishedAt ? 'done' : startedAt ? 'in-progress' : 'pending');\n\n // \"Running the tests\" while it runs, \"Run the tests\" before and after — Claude Code's pair.\n // The wording not shown is kept too, for the title to hold its room.\n const active = str(pick(r, ACTIVE_FORM_KEYS));\n let altTitle: string | undefined;\n if (active && active !== title) {\n if (status === 'in-progress') [title, altTitle] = [active, title];\n else altTitle = active;\n }\n\n const error = str(r['error']);\n const detail = (status === 'failed' ? error : undefined)\n ?? str(pick(r, DETAIL_KEYS.filter((key) => key !== titleKey)));\n\n const seconds = num(pick(r, SECONDS_KEYS));\n const ms = num(pick(r, MS_KEYS));\n let duration = seconds ?? (ms !== undefined ? ms / 1000 : undefined);\n if (duration === undefined && startedAt && finishedAt && finishedAt >= startedAt) duration = (finishedAt.getTime() - startedAt.getTime()) / 1000;\n if (duration !== undefined && duration < 0) duration = undefined;\n const id = str(pick(r, ['id', 'key', 'uuid']));\n\n return {\n title,\n ...(altTitle ? { altTitle } : {}),\n status,\n ...(detail ? { detail } : {}),\n ...(startedAt ? { startedAt } : {}),\n ...(finishedAt ? { finishedAt } : {}),\n ...(duration !== undefined ? { duration } : {}),\n ...(substeps.length ? { substeps } : {}),\n ...(id ? { id } : {}),\n };\n}\n\n/** At most fifty steps, sub-steps counted: a long list keeps its first steps whole, then stops. */\nfunction cap(steps: PlanStep[]): PlanStep[] {\n let left = PLAN_STEPS_SHOWN;\n const kept: PlanStep[] = [];\n for (const step of steps) {\n if (left <= 0) break;\n left -= 1;\n if (!step.substeps) {\n kept.push(step);\n continue;\n }\n const substeps = step.substeps.slice(0, left);\n left -= substeps.length;\n const copy: PlanStep = { ...step, substeps };\n if (!substeps.length) delete copy.substeps;\n kept.push(copy);\n }\n return kept;\n}\n\n/**\n * The card's data from the loose shape `map` returns — `{ title, goal, steps }` (also\n * `todos`, `tasks`, `items`, `plan`…), a list of steps or of strings, a Markdown\n * checklist, LangGraph's `{ plan, past_steps }` — or `null` when it holds no step.\n */\nexport function readPlan(raw: unknown): PlanData | null {\n if (typeof raw === 'string') {\n const parsed = parseText(raw);\n return parsed ? { ...(parsed.title ? { title: parsed.title } : {}), steps: cap(parsed.steps) } : null;\n }\n if (Array.isArray(raw)) {\n const steps = cap(raw.map((item) => readStep(item, 0)).filter(notNull));\n return steps.length ? { steps } : null;\n }\n if (!isObject(raw)) return null;\n const r = raw;\n const title = str(pick(r, PLAN_TITLE_KEYS));\n const goal = str(pick(r, GOAL_KEYS));\n const updatedAt = toDate(pick(r, UPDATED_KEYS)) ?? undefined;\n const meta = { ...(title ? { title } : {}), ...(goal ? { goal } : {}), ...(updatedAt ? { updatedAt } : {}), ...(r['timed'] === true ? { timed: true } : {}) };\n\n // A plan wrapped once more: `{ plan: { title, steps } }`. The outer words fill what it lacks.\n if (isObject(r['plan'])) {\n const inner = readPlan(r['plan']);\n return inner ? { ...meta, ...inner } : null;\n }\n\n const past = PAST_KEYS.map((key) => r[key]).find(Array.isArray) ?? [];\n const list = LIST_KEYS.map((key) => r[key]).find((value) => Array.isArray(value) || typeof value === 'string');\n const ahead = Array.isArray(list) ? list.map((item) => readStep(item, 0)).filter(notNull)\n : typeof list === 'string' ? parseText(list)?.steps ?? []\n : [];\n const steps = cap([...past.map((item) => readStep(item, 0, 'done')).filter(notNull), ...ahead]);\n return steps.length ? { ...meta, steps } : null;\n}\n\n// ── Progress ─────────────────────────────────────────────────────────────────\n\nconst allSteps = (data: PlanData) => data.steps.flatMap((step) => [step, ...(step.substeps ?? [])]);\n\n/** Seconds from the first start to the last date the plan holds, else the durations added up. */\nfunction elapsedOf(data: PlanData): number | undefined {\n const steps = allSteps(data);\n const starts = steps.map((s) => s.startedAt?.getTime()).filter(notNull);\n if (starts.length) {\n const ends = steps.flatMap((s) => [s.startedAt?.getTime(), s.finishedAt?.getTime()]).filter(notNull);\n if (data.updatedAt) ends.push(data.updatedAt.getTime());\n const span = (Math.max(...ends) - Math.min(...starts)) / 1000;\n if (span > 0) return span;\n }\n const sum = data.steps.reduce((total, s) => total + (s.duration ?? (s.substeps ?? []).reduce((t, sub) => t + (sub.duration ?? 0), 0)), 0);\n return sum > 0 ? sum : undefined;\n}\n\n/** How far the plan has got, counted over its top-level steps. */\nexport function planProgress(data: PlanData): PlanProgress {\n const steps = data.steps;\n const count = (status: PlanStatus) => steps.filter((s) => s.status === status).length;\n const total = steps.length;\n const done = count('done');\n const skipped = count('skipped');\n const finished = done + skipped;\n let currentIndex = steps.findIndex((s) => s.status === 'in-progress');\n if (currentIndex < 0) currentIndex = steps.findIndex((s) => s.status === 'pending');\n if (currentIndex < 0) currentIndex = steps.findIndex((s) => s.status === 'blocked');\n const stoppedIndex = currentIndex < 0 ? steps.findIndex((s) => s.status === 'failed') : -1;\n const elapsed = elapsedOf(data);\n return {\n total,\n done,\n skipped,\n failed: count('failed'),\n blocked: count('blocked'),\n finished,\n ratio: total ? finished / total : 0,\n ...(currentIndex >= 0 ? { current: steps[currentIndex]!, currentIndex } : {}),\n ...(stoppedIndex >= 0 ? { stoppedIndex } : {}),\n ...(elapsed !== undefined ? { elapsed } : {}),\n };\n}\n\n/** A plan that says when its steps ran, or that it will: its rows keep a slot for the time. */\nconst isTimed = (data: PlanData) => data.timed === true || allSteps(data).some((s) => s.startedAt || s.finishedAt || s.duration !== undefined);\n\n// ── Writing times ────────────────────────────────────────────────────────────\n\n/** A duration, its two largest units in their narrow form: `45s`, `1m 20s`, `2h 5m`, `0.4s`. */\nexport function durationText(config: AparteConfig, seconds: number): string {\n const tag = intlTag(config);\n const unit = (value: number, name: string, digits = 0) =>\n new Intl.NumberFormat(tag, { style: 'unit', unit: name, unitDisplay: 'narrow', maximumFractionDigits: digits }).format(value);\n const s = Math.max(0, seconds);\n if (s < 10 && !Number.isInteger(s)) return unit(s, 'second', 1);\n let rest = Math.round(s);\n if (rest < 60) return unit(rest, 'second');\n const parts: string[] = [];\n let slots = 0;\n for (const [name, size] of [['day', 86_400], ['hour', 3_600], ['minute', 60], ['second', 1]] as const) {\n const n = Math.floor(rest / size);\n rest -= n * size;\n if (n > 0) parts.push(unit(n, name));\n if (n > 0 || slots) slots += 1;\n if (slots === 2) break;\n }\n try {\n return new Intl.ListFormat(tag, { type: 'unit', style: 'narrow' }).format(parts);\n } catch {\n return parts.join(' ');\n }\n}\n\n/** A duration as `<time datetime>` takes it: `PT80S`. */\nconst isoDuration = (seconds: number) => `PT${Math.round(seconds * 1000) / 1000}S`;\n\n/** The time of day a step started; with its seconds when the whole run fits in an hour, where they tell steps apart. */\nfunction clockText(config: AparteConfig, date: Date, seconds: boolean): string {\n return new Intl.DateTimeFormat(intlTag(config), { hour: 'numeric', minute: '2-digit', ...(seconds ? { second: '2-digit' } : {}) }).format(date);\n}\n\n/** Whether the plan's dates all fall within an hour. */\nfunction withinAnHour(data: PlanData): boolean {\n const times = allSteps(data).flatMap((s) => [s.startedAt?.getTime(), s.finishedAt?.getTime()]).filter(notNull);\n return !times.length || Math.max(...times) - Math.min(...times) < 3_600_000;\n}\n\n// ── Drawing ──────────────────────────────────────────────────────────────────\n\nconst n = (config: AparteConfig, value: number) => formatNumber(config, value);\nconst percentOf = (part: number, total: number) => `${total ? (part / total) * 100 : 0}%`;\nconst FINISHED = new Set<PlanStatus>(['done', 'failed', 'skipped']);\n\nfunction statusGlyph(status: PlanStatus, className = 'aparte-plan__glyph'): HTMLElement {\n return glyph(STATUS_GLYPHS[status], `aparte-rich-card__glyph ${className}`);\n}\n\n/**\n * The step's title, holding the room of its bold form: the text and an invisible bold\n * copy share one grid cell, so the step that starts running turns bold without its line\n * growing or wrapping differently — the row the reader is looking at does not move. A step\n * with two wordings (\"Update\" / \"Updating the reference pages\") holds the room of the\n * other one too, in a second invisible copy.\n */\nfunction titleOf(step: PlanStep): HTMLElement {\n return el('span', { class: 'aparte-plan__step-title', attrs: { 'data-text': step.title, 'data-alt': step.altTitle } },\n el('span', { class: 'aparte-plan__step-title-text', text: step.title }));\n}\n\n/** The status in words, for a screen reader: the glyph and its colour say it to the eye only. */\nfunction statusText(ctx: CardContext, step: PlanStep): HTMLElement {\n const node = el('span', { class: 'aparte-sr-only aparte-plan__status' });\n ctx.bind(node, () => ` (${say(ctx.config, STATUS_WORD[step.status])})`);\n return node;\n}\n\nconst detailOf = (step: PlanStep) => (step.detail ? el('div', { class: 'aparte-plan__detail', text: step.detail }) : null);\n\n/**\n * The widest duration the language writes in two units — `59m 59s` in English,\n * `59 Min., 59 Sek.` in German. The checklist's duration slot holds its width (an invisible\n * copy, `data-reserve`): a fixed `3.5rem` held English and not German, so the first long\n * duration widened its slot and could wrap its title onto a second line.\n */\nfunction widestDuration(config: AparteConfig): string {\n return [3_599, 86_340, 604_799].map((s) => durationText(config, s)).reduce((a, b) => (b.length > a.length ? b : a));\n}\n\n/** How long a finished step took — always drawn in a timed plan, empty until it finished, so filling it moves nothing. */\nfunction durationOf(ctx: CardContext, step: PlanStep): HTMLElement {\n const shown = step.duration !== undefined && FINISHED.has(step.status) ? step.duration : undefined;\n const node = el('time', { class: 'aparte-plan__duration', attrs: { datetime: shown !== undefined ? isoDuration(shown) : undefined } });\n if (shown !== undefined) ctx.bind(node, () => durationText(ctx.config, shown));\n ctx.bind(node, () => widestDuration(ctx.config), 'data-reserve');\n return node;\n}\n\n/** The header: the title or the goal, \"3 of 7 done\", what was skipped or failed, the time taken — and, with `bar`, the bar. */\nfunction head(ctx: CardContext, data: PlanData, progress: PlanProgress, bar: boolean): HTMLElement {\n const heading = data.title ?? data.goal;\n const count = el('span', { class: 'aparte-plan__count' });\n ctx.bind(count, () => say(ctx.config, 'cardsPlanProgress', { done: n(ctx.config, progress.done), total: n(ctx.config, progress.total) }));\n const note = (kind: 'skipped' | 'failed', key: WordKey, value: number) => {\n if (!value) return null;\n const node = el('span', { class: 'aparte-plan__note', attrs: { 'data-kind': kind } });\n ctx.bind(node, () => say(ctx.config, key, { n: n(ctx.config, value) }));\n return node;\n };\n let elapsed: HTMLElement | null = null;\n if (progress.elapsed !== undefined) {\n const seconds = progress.elapsed;\n const text = el('span', { class: 'aparte-plan__elapsed-text' });\n ctx.bind(text, () => say(ctx.config, 'cardsPlanElapsed', { time: durationText(ctx.config, seconds) }));\n elapsed = el('span', { class: 'aparte-plan__elapsed' }, glyph(CLOCK, 'aparte-rich-card__glyph aparte-plan__clock'), text);\n }\n let track: HTMLElement | null = null;\n if (bar) {\n track = el('div', { class: 'aparte-plan__bar', attrs: { 'aria-hidden': 'true', 'data-complete': progress.total > 0 && progress.finished === progress.total } },\n el('span', { class: 'aparte-plan__bar-done' }),\n el('span', { class: 'aparte-plan__bar-skipped' }));\n track.style.setProperty('--aparte-plan-done-share', percentOf(progress.done, progress.total));\n track.style.setProperty('--aparte-plan-skipped-share', percentOf(progress.skipped, progress.total));\n }\n return el('div', { class: 'aparte-plan__head' },\n heading ? el('div', { class: 'aparte-plan__heading' },\n el('div', { class: 'aparte-plan__title', text: heading }),\n data.title && data.goal ? el('div', { class: 'aparte-plan__goal', text: data.goal }) : null) : null,\n el('div', { class: 'aparte-plan__meter' },\n count,\n note('skipped', 'cardsPlanSkipped', progress.skipped),\n note('failed', 'cardsPlanFailed', progress.failed),\n elapsed),\n track);\n}\n\n/** A row of the checklist, and its sub-steps under it. */\nfunction stepNode(ctx: CardContext, step: PlanStep, { timed, current = false, sub = false }: { timed: boolean; current?: boolean; sub?: boolean }): HTMLLIElement {\n const row = el('div', { class: 'aparte-plan__row' },\n statusGlyph(step.status),\n el('div', { class: 'aparte-plan__text' }, titleOf(step), statusText(ctx, step), detailOf(step)),\n timed ? durationOf(ctx, step) : null);\n const node = el('li', {\n class: `aparte-plan__step${sub ? ' aparte-plan__step--sub' : ''}`,\n attrs: { 'data-status': step.status, 'aria-current': current ? 'step' : undefined, 'data-step-id': step.id },\n }, row);\n if (step.substeps?.length) {\n node.append(el('ol', { class: 'aparte-plan__substeps', attrs: { role: 'list' } },\n ...step.substeps.map((s) => stepNode(ctx, s, { timed, sub: true }))));\n }\n return node;\n}\n\n/** The `checklist` variant: the header and its bar, then a row per step. */\nfunction checklist(ctx: CardContext, data: PlanData, progress: PlanProgress): Node[] {\n const timed = isTimed(data);\n const list = el('ol', { class: 'aparte-plan__steps', attrs: { role: 'list', 'data-timed': timed } },\n ...data.steps.map((step, i) => stepNode(ctx, step, { timed, current: i === progress.currentIndex })));\n return [head(ctx, data, progress, true), list];\n}\n\n/**\n * The time line of a timeline step: when it started, and how long it took. In a timed plan\n * every step has it, empty until it starts — the steps keep one rhythm, and a step that\n * starts fills its line instead of growing one.\n */\nfunction timesOf(ctx: CardContext, step: PlanStep, seconds: boolean): HTMLElement {\n const started = step.startedAt;\n const start = el('time', { class: 'aparte-plan__start', attrs: { datetime: started?.toISOString() } });\n if (started) ctx.bind(start, () => clockText(ctx.config, started, seconds));\n return el('div', { class: 'aparte-plan__meta' }, start, durationOf(ctx, step));\n}\n\n/** The `timeline` variant: a rail with a node per step, its time beside it. */\nfunction timeline(ctx: CardContext, data: PlanData, progress: PlanProgress): Node[] {\n const timed = isTimed(data);\n const seconds = withinAnHour(data);\n const list = el('ol', { class: 'aparte-plan__timeline', attrs: { role: 'list', 'data-timed': timed } });\n data.steps.forEach((step, i) => {\n const node = el('span', { class: 'aparte-plan__node' },\n glyph(step.status === 'in-progress' ? NODE_CURRENT : STATUS_GLYPHS[step.status], 'aparte-rich-card__glyph aparte-plan__node-glyph'));\n const body = el('div', { class: 'aparte-plan__event-body' },\n titleOf(step),\n statusText(ctx, step),\n timed ? timesOf(ctx, step, seconds) : null,\n detailOf(step),\n step.substeps?.length\n ? el('ol', { class: 'aparte-plan__substeps', attrs: { role: 'list' } }, ...step.substeps.map((s) => stepNode(ctx, s, { timed: false, sub: true })))\n : null);\n list.append(el('li', {\n class: 'aparte-plan__event',\n attrs: { 'data-status': step.status, 'aria-current': i === progress.currentIndex ? 'step' : undefined, 'data-step-id': step.id },\n }, node, body));\n });\n return [head(ctx, data, progress, false), list];\n}\n\nconst SVG = 'http://www.w3.org/2000/svg';\n\n/** The progress ring: what is done, then what was skipped, round a faint track. Numbers only. */\nfunction ring(progress: PlanProgress): HTMLElement {\n const svg = document.createElementNS(SVG, 'svg');\n svg.setAttribute('viewBox', '0 0 24 24');\n svg.setAttribute('class', 'aparte-plan__ring-svg');\n const arc = (cls: string, share?: number, offset = 0) => {\n const circle = document.createElementNS(SVG, 'circle');\n for (const [name, value] of [['cx', '12'], ['cy', '12'], ['r', '9.5'], ['pathLength', '100'], ['class', cls]]) circle.setAttribute(name!, value!);\n if (share !== undefined) {\n circle.setAttribute('stroke-dasharray', `${share} 100`);\n circle.setAttribute('stroke-dashoffset', String(-offset));\n }\n return circle;\n };\n const done = progress.total ? (progress.done / progress.total) * 100 : 0;\n const skipped = progress.total ? (progress.skipped / progress.total) * 100 : 0;\n svg.append(arc('aparte-plan__ring-track'), arc('aparte-plan__ring-done', done), arc('aparte-plan__ring-skipped', skipped, done));\n return el('span', { class: 'aparte-plan__ring', attrs: { 'aria-hidden': 'true', 'data-complete': progress.total > 0 && progress.finished === progress.total } }, svg);\n}\n\n/**\n * The `compact` variant: one line — the step under way (\"Step 3 of 7: Running the tests\"),\n * else where it stopped, else the plan's name and \"All steps done\" — a ring and the percentage.\n */\nfunction compact(ctx: CardContext, data: PlanData, progress: PlanProgress): Node[] {\n const index = progress.currentIndex ?? progress.stoppedIndex;\n const focus = index !== undefined ? data.steps[index] : undefined;\n const step = el('span', { class: 'aparte-plan__compact-step' });\n const parts: HTMLElement[] = [];\n if (focus) {\n ctx.bind(step, () => say(ctx.config, 'cardsPlanStepNow', { n: n(ctx.config, index! + 1), total: n(ctx.config, progress.total) }));\n parts.push(step, el('span', { class: 'aparte-plan__compact-title', text: focus.title }));\n } else {\n ctx.word(step, 'cardsPlanAllDone', W.cardsPlanAllDone);\n const name = data.title ?? data.goal;\n if (name) parts.push(el('span', { class: 'aparte-plan__compact-title', text: name }));\n parts.push(step);\n }\n const percent = el('span', { class: 'aparte-plan__percent' });\n ctx.bind(percent, () => formatPercent(ctx.config, progress.ratio));\n const status: PlanStatus = focus ? focus.status : 'done';\n return [el('div', { class: 'aparte-plan__compact', attrs: { 'data-status': status } },\n statusGlyph(status, 'aparte-plan__glyph aparte-plan__compact-glyph'),\n el('span', { class: 'aparte-plan__compact-text' }, ...parts),\n ring(progress),\n percent)];\n}\n\nfunction render(data: PlanData, ctx: CardContext<PlanVariant>): Node[] {\n const progress = planProgress(data);\n if (ctx.variant === 'timeline') return timeline(ctx, data, progress);\n if (ctx.variant === 'compact') return compact(ctx, data, progress);\n return checklist(ctx, data, progress);\n}\n\nfunction skeleton(partial: Partial<PlanData> | null, ctx: CardContext<PlanVariant>): Node[] {\n // The call's input may already be the plan — a todo tool's is: draw it while it runs.\n if (partial?.steps?.length) return render(partial as PlanData, ctx);\n const block = (cls: string, kind: 'text' | 'rect' | 'circle' = 'rect') =>\n el('span', { class: `aparte-skeleton aparte-skeleton--${kind} ${cls}`, attrs: { 'aria-hidden': 'true' } });\n const heading = partial?.title ?? partial?.goal;\n if (ctx.variant === 'compact') {\n return [el('div', { class: 'aparte-plan__compact' },\n block('aparte-plan__sk-glyph', 'circle'),\n el('span', { class: 'aparte-plan__compact-text' },\n heading ? el('span', { class: 'aparte-plan__compact-title', text: heading }) : block('aparte-plan__sk-line', 'text')),\n block('aparte-plan__sk-ring', 'circle'))];\n }\n const top = el('div', { class: 'aparte-plan__head' },\n heading ? el('div', { class: 'aparte-plan__heading' }, el('div', { class: 'aparte-plan__title', text: heading })) : block('aparte-plan__sk-title', 'text'),\n el('div', { class: 'aparte-plan__meter' }, block('aparte-plan__sk-count', 'text')),\n ctx.variant === 'checklist' ? el('div', { class: 'aparte-plan__bar', attrs: { 'aria-hidden': 'true' } }) : null);\n const rows = [0, 1, 2, 3];\n if (ctx.variant === 'timeline') {\n return [top, el('ol', { class: 'aparte-plan__timeline', attrs: { 'aria-hidden': 'true' } }, ...rows.map(() =>\n el('li', { class: 'aparte-plan__event', attrs: { 'data-status': 'pending' } },\n el('span', { class: 'aparte-plan__node' }, block('aparte-plan__sk-node', 'circle')),\n el('div', { class: 'aparte-plan__event-body' }, block('aparte-plan__sk-line', 'text'), block('aparte-plan__sk-meta', 'text')))))];\n }\n return [top, el('ol', { class: 'aparte-plan__steps', attrs: { 'aria-hidden': 'true' } }, ...rows.map(() =>\n el('li', { class: 'aparte-plan__step' },\n el('div', { class: 'aparte-plan__row' }, block('aparte-plan__sk-glyph', 'circle'), block('aparte-plan__sk-line', 'text')))))];\n}\n\nfunction cardLabel(data: Partial<PlanData> | null, ctx: CardContext): string {\n return data?.title ?? data?.goal ?? say(ctx.config, 'cardsPlanTitle');\n}\n\n/** The marker a step's line carries in the fallback — and which `readPlan` reads back. */\nconst MARK: Record<PlanStatus, string> = { done: '[x]', pending: '[ ]', 'in-progress': '[>]', failed: '[!]', skipped: '[-]', blocked: '[~]' };\n\n/** A detail kept short enough for one line of the fallback. */\nconst clip = (text: string) => (text.length > 120 ? `${text.slice(0, 119).trimEnd()}…` : text);\n\n/**\n * \"Deploy: 3/7 done — now: Run the tests\", then a line per step with its marker — what a\n * model reads back, and what a reader without the card gets.\n */\nfunction fallback(data: PlanData, ctx: CardContext): string {\n const progress = planProgress(data);\n const summary = say(ctx.config, 'cardsPlanSummary', { done: n(ctx.config, progress.done), total: n(ctx.config, progress.total) });\n const now = progress.current ? ` — ${say(ctx.config, 'cardsPlanNow', { step: progress.current.title })}` : '';\n const line = (step: PlanStep, indent: string) => {\n const why = (step.status === 'failed' || step.status === 'blocked') && step.detail ? ` — ${clip(step.detail)}` : '';\n return `${indent}- ${MARK[step.status]} ${step.title}${why}`;\n };\n const lines = data.steps.flatMap((step) => [line(step, ''), ...(step.substeps ?? []).map((sub) => line(sub, ' '))]);\n return [`${cardLabel(data, ctx)}: ${summary}${now}`, ...lines].join('\\n');\n}\n\nexport const planCard = defineCard<PlanData, PlanVariant>({\n name: 'plan',\n variants: ['checklist', 'timeline', 'compact'],\n styles: planStyles,\n icon: PLAN,\n normalize: readPlan,\n render,\n skeleton,\n label: cardLabel,\n fallback,\n partial: (input) => {\n const plan = readPlan(input);\n if (plan) return plan;\n const about = str(input['title'] ?? input['goal'] ?? input['task'] ?? input['objective'] ?? input['query']);\n return about ? { title: about } : null;\n },\n});\n\n/** Draw an agent's plan or task list as this card — returns the function that stops it. */\nexport function setupPlanCard(options: CardSetupOptions<PlanData, PlanVariant> = {}): () => void {\n return planCard.setup(options);\n}\n"],"names":["steps","n","svg"],"mappings":";AAUA,MAAM,MAAM,CAAC,MAAc,cACvB,OAAO,YAAY,WAAW,SAAS,MAAM,EAAE,+JAA+J,IAAI;AAGtN,MAAM,UAAU,IAAI,iCAAiC;AAGrD,MAAM,UAAU,IAAI,gFAAgF,sBAAsB;AAG1H,MAAM,OAAO,IAAI,gIAAgI;AAGjJ,MAAM,SAAS,IAAI,2IAA2I;AAG9J,MAAM,UAAU,IAAI,mFAAmF;AAGvG,MAAM,UAAU,IAAI,wGAAwG;AAGrH,MAAM,gBAAgB;AAAA,EACzB,SAAS;AAAA,EACT,eAAe;AAAA,EACf,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AACb;AAGO,MAAM,eAAe,IAAI,kGAAkG;AAG3H,MAAM,QAAQ,IAAI,iEAAiE;AAGnF,MAAM,OAAO,IAAI,kPAAkP;AChD1Q,MAAA,aAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AC6If,MAAM,IAAI;AAAA,EACN,gBAAgB,EAAE,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,IAAI,SAAS,IAAI,MAAM,IAAI,MAAM,IAAI,SAAS,IAAI,QAAQ,IAAI,KAAA;AAAA,EAChI,mBAAmB;AAAA,IACf,IAAI;AAAA,IAA0B,IAAI;AAAA,IAAgC,IAAI;AAAA,IAA+B,IAAI;AAAA,IACzG,IAAI;AAAA,IAAgC,IAAI;AAAA,IAAsB,IAAI;AAAA,IAAyB,IAAI;AAAA,IAC/F,IAAI;AAAA,IAA+B,IAAI;AAAA,EAAA;AAAA,EAE3C,kBAAkB;AAAA,IACd,IAAI;AAAA,IAAe,IAAI;AAAA,IAAkB,IAAI;AAAA,IAAqB,IAAI;AAAA,IAAiB,IAAI;AAAA,IAC3F,IAAI;AAAA,IAAa,IAAI;AAAA,IAAY,IAAI;AAAA,IAAkB,IAAI;AAAA,IAAkB,IAAI;AAAA,EAAA;AAAA,EAErF,iBAAiB;AAAA,IACb,IAAI;AAAA,IAAc,IAAI;AAAA,IAAgB,IAAI;AAAA,IAAuB,IAAI;AAAA,IAAiB,IAAI;AAAA,IAC1F,IAAI;AAAA,IAAW,IAAI;AAAA,IAAW,IAAI;AAAA,IAAe,IAAI;AAAA,IAAe,IAAI;AAAA,EAAA;AAAA,EAE5E,kBAAkB;AAAA,IACd,IAAI;AAAA,IAAkB,IAAI;AAAA,IAAkB,IAAI;AAAA,IAAiB,IAAI;AAAA,IAAoB,IAAI;AAAA,IAC7F,IAAI;AAAA,IAAe,IAAI;AAAA,IAAa,IAAI;AAAA,IAAmB,IAAI;AAAA,IAAkB,IAAI;AAAA,EAAA;AAAA,EAEzF,kBAAkB;AAAA,IACd,IAAI;AAAA,IAAwB,IAAI;AAAA,IAA2B,IAAI;AAAA,IAA4B,IAAI;AAAA,IAC/F,IAAI;AAAA,IAAyB,IAAI;AAAA,IAAqB,IAAI;AAAA,IAAsB,IAAI;AAAA,IACpF,IAAI;AAAA,IAAuB,IAAI;AAAA,EAAA;AAAA,EAEnC,kBAAkB;AAAA,IACd,IAAI;AAAA,IAAkB,IAAI;AAAA,IAAoC,IAAI;AAAA,IAA0B,IAAI;AAAA,IAChG,IAAI;AAAA,IAA4B,IAAI;AAAA,IAAe,IAAI;AAAA,IAAY,IAAI;AAAA,IACvE,IAAI;AAAA,IAAsB,IAAI;AAAA,EAAA;AAAA,EAElC,wBAAwB,EAAE,IAAI,SAAS,IAAI,WAAW,IAAI,SAAS,IAAI,aAAa,IAAI,WAAW,IAAI,OAAO,IAAI,MAAM,IAAI,WAAW,IAAI,aAAa,IAAI,KAAA;AAAA,EAC5J,2BAA2B,EAAE,IAAI,eAAe,IAAI,YAAY,IAAI,aAAa,IAAI,YAAY,IAAI,YAAY,IAAI,OAAO,IAAI,QAAQ,IAAI,gBAAgB,IAAI,eAAe,IAAI,MAAA;AAAA,EACnL,qBAAqB,EAAE,IAAI,QAAQ,IAAI,WAAW,IAAI,YAAY,IAAI,SAAS,IAAI,SAAS,IAAI,MAAM,IAAI,MAAM,IAAI,aAAa,IAAI,UAAU,IAAI,MAAA;AAAA,EACnJ,uBAAuB,EAAE,IAAI,UAAU,IAAI,SAAS,IAAI,kBAAkB,IAAI,WAAW,IAAI,gBAAgB,IAAI,MAAM,IAAI,MAAM,IAAI,UAAU,IAAI,UAAU,IAAI,KAAA;AAAA,EACjK,wBAAwB,EAAE,IAAI,WAAW,IAAI,UAAU,IAAI,gBAAgB,IAAI,WAAW,IAAI,WAAW,IAAI,QAAQ,IAAI,OAAO,IAAI,YAAY,IAAI,aAAa,IAAI,MAAA;AAAA,EACrK,wBAAwB,EAAE,IAAI,WAAW,IAAI,UAAU,IAAI,aAAa,IAAI,aAAa,IAAI,YAAY,IAAI,SAAS,IAAI,OAAO,IAAI,aAAa,IAAI,iBAAiB,IAAI,KAAA;AAAA,EAC3K,kBAAkB;AAAA,IACd,IAAI;AAAA,IAAuB,IAAI;AAAA,IAA4B,IAAI;AAAA,IAA2B,IAAI;AAAA,IAC9F,IAAI;AAAA,IAA6B,IAAI;AAAA,IAAqB,IAAI;AAAA,IAAqB,IAAI;AAAA,IACvF,IAAI;AAAA,IAA4B,IAAI;AAAA,EAAA;AAAA,EAExC,cAAc;AAAA,IACV,IAAI;AAAA,IAAe,IAAI;AAAA,IAAqB,IAAI;AAAA,IAAiB,IAAI;AAAA,IAAiB,IAAI;AAAA,IAC1F,IAAI;AAAA,IAAc,IAAI;AAAA,IAAc,IAAI;AAAA,IAAiB,IAAI;AAAA,IAAkB,IAAI;AAAA,EAAA;AAE3F;AAIA,MAAM,MAAM,CAAC,QAAsB,KAAc,SAAkC,KAAK,QAAQ,KAAK,EAAE,GAAG,GAAG,IAAI;AAEjH,MAAM,cAA2C;AAAA,EAC7C,SAAS;AAAA,EACT,eAAe;AAAA,EACf,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AACb;AAGO,MAAM,mBAAmB;AAMhC,MAAM,WAAW,CAAC,UAAiC,CAAC,CAAC,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAE/G,MAAM,UAAU,CAAI,UAA4C,UAAU,QAAQ,UAAU;AAG5F,SAAS,KAAK,GAAQ,MAAkC;AACpD,aAAW,OAAO,KAAM,KAAI,EAAE,GAAG,MAAM,UAAa,EAAE,GAAG,MAAM,QAAQ,EAAE,GAAG,MAAM,GAAI,QAAO,EAAE,GAAG;AAClG,SAAO;AACX;AAGA,MAAM,iBAAwD;AAAA,EAC1D,SAAS,CAAC,WAAW,QAAQ,SAAS,QAAQ,UAAU,WAAW,eAAe,cAAc,OAAO,WAAW,aAAa,YAAY,SAAS,QAAQ,SAAS;AAAA,EACrK,eAAe,CAAC,eAAe,cAAc,YAAY,SAAS,WAAW,UAAU,WAAW,WAAW,WAAW,aAAa,WAAW,cAAc,WAAW;AAAA,EACzK,MAAM,CAAC,QAAQ,aAAa,YAAY,WAAW,aAAa,cAAc,YAAY,MAAM,UAAU,YAAY,UAAU,SAAS;AAAA,EACzI,QAAQ,CAAC,UAAU,QAAQ,WAAW,SAAS,WAAW,WAAW,WAAW,aAAa,SAAS;AAAA,EACtG,SAAS,CAAC,WAAW,QAAQ,aAAa,YAAY,WAAW,cAAc,WAAW,YAAY,UAAU,YAAY,SAAS;AAAA,EACrI,SAAS,CAAC,WAAW,WAAW,UAAU,UAAU,SAAS,QAAQ,eAAe,kBAAkB,kBAAkB,mBAAmB;AAC/I;AAEA,MAAM,YAAY,IAAI;AAAA,EACjB,OAAO,QAAQ,cAAc,EAA6C,QAAQ,CAAC,CAAC,QAAQ,KAAK,MAAM,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,MAAM,CAAU,CAAC;AAClJ;AAGO,SAAS,WAAW,OAAwC;AAC/D,MAAI,OAAO,UAAU,UAAW,QAAO,QAAQ,SAAS;AACxD,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,SAAO,UAAU,IAAI,MAAM,KAAA,EAAO,cAAc,QAAQ,WAAW,GAAG,CAAC;AAC3E;AAEA,MAAM,cAAc,CAAC,UAAU,SAAS,SAAS,OAAO;AACxD,MAAM,aAAa,CAAC,QAAQ,aAAa,YAAY,WAAW,YAAY,UAAU,WAAW,eAAe,cAAc;AAC9H,MAAM,eAAe,CAAC,UAAU,WAAW,WAAW,cAAc,aAAa;AACjF,MAAM,aAAa,CAAC,SAAS,WAAW,QAAQ,SAAS,QAAQ,QAAQ,QAAQ,UAAU,aAAa;AACxG,MAAM,mBAAmB,CAAC,cAAc,eAAe,eAAe,cAAc;AACpF,MAAM,cAAc,CAAC,UAAU,WAAW,eAAe,QAAQ,SAAS,WAAW,UAAU,UAAU,eAAe,WAAW,QAAQ;AAC3I,MAAM,aAAa,CAAC,aAAa,cAAc,aAAa,cAAc,aAAa,cAAc,SAAS,OAAO;AACrH,MAAM,WAAW,CAAC,cAAc,eAAe,eAAe,gBAAgB,WAAW,YAAY,WAAW,YAAY,UAAU,WAAW,KAAK;AACtJ,MAAM,eAAe,CAAC,YAAY,mBAAmB,oBAAoB,eAAe,WAAW,WAAW,MAAM;AACpH,MAAM,UAAU,CAAC,cAAc,eAAe,aAAa,cAAc,UAAU,SAAS;AAC5F,MAAM,WAAW,CAAC,YAAY,YAAY,aAAa,YAAY,YAAY,YAAY,SAAS,SAAS,SAAS,WAAW;AACjI,MAAM,YAAY,CAAC,SAAS,SAAS,SAAS,SAAS,aAAa,UAAU,UAAU,YAAY,MAAM;AAC1G,MAAM,YAAY,CAAC,cAAc,aAAa,mBAAmB,gBAAgB;AACjF,MAAM,kBAAkB,CAAC,SAAS,QAAQ,SAAS;AACnD,MAAM,YAAY,CAAC,QAAQ,aAAa,QAAQ,SAAS,SAAS,WAAW,eAAe,eAAe,SAAS;AACpH,MAAM,eAAe,CAAC,aAAa,cAAc,eAAe,gBAAgB,QAAQ,SAAS,WAAW;AAG5G,SAAS,SAAS,GAAgC;AAC9C,QAAM,OAAO,WAAW,KAAK,GAAG,WAAW,CAAC;AAC5C,MAAI,KAAM,QAAO;AACjB,MAAI,EAAE,QAAQ,MAAM,QAAQ,EAAE,OAAO,MAAM,QAAQ,IAAI,EAAE,OAAO,CAAC,EAAG,QAAO;AAC3E,MAAI,EAAE,SAAS,MAAM,QAAQ,EAAE,WAAW,MAAM,QAAQ,EAAE,UAAU,MAAM,KAAM,QAAO;AACvF,MAAI,EAAE,SAAS,MAAM,KAAM,QAAO;AAClC,QAAM,SAAS,aAAa,KAAK,CAAC,QAAQ,EAAE,GAAG,MAAM,IAAI;AACzD,aAAW,OAAO,YAAY;AAC1B,QAAI,OAAO,EAAE,GAAG,MAAM,UAAW,QAAO,EAAE,GAAG,IAAI,SAAS,SAAS,gBAAgB;AAAA,EACvF;AACA,SAAO,SAAS,gBAAgB;AACpC;AAMA,SAAS,aAAa,MAA8B;AAChD,QAAM,MAAM,CAAC,WAAuB,KAAK,KAAK,CAAC,MAAM,EAAE,WAAW,MAAM;AACxE,MAAI,KAAK,MAAM,CAAC,MAAM,EAAE,WAAW,UAAU,EAAE,WAAW,SAAS,EAAG,QAAO,IAAI,MAAM,IAAI,SAAS;AACpG,MAAI,IAAI,aAAa,EAAG,QAAO;AAC/B,MAAI,IAAI,QAAQ,EAAG,QAAO;AAC1B,MAAI,IAAI,MAAM,KAAK,IAAI,SAAS,EAAG,QAAO;AAC1C,MAAI,IAAI,SAAS,EAAG,QAAO;AAC3B,SAAO;AACX;AAGA,MAAM,OAAO;AAEb,MAAM,OAAO;AAEb,MAAM,MAAkC,EAAE,KAAK,WAAW,GAAG,QAAQ,GAAG,QAAQ,KAAK,QAAQ,KAAK,QAAQ,KAAK,eAAe,KAAK,UAAU,KAAK,WAAW,KAAK,UAAA;AAElK,SAAS,SAAS,MAA6D;AAC3E,QAAM,QAAQ,KAAK,KAAK,IAAI;AAC5B,QAAM,QAAQ,MAAM,CAAC;AACrB,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,EAAE,OAAO,GAAI,MAAM,CAAC,MAAM,SAAY,EAAE,QAAQ,IAAI,MAAM,CAAC,CAAC,EAAA,IAAO,CAAA,EAAC;AAC/E;AAOA,SAAS,UAAU,MAA4D;AAC3E,QAAM,QAAQ,KAAK,MAAM,OAAO,EAAE,OAAO,CAAC,SAAS,KAAK,MAAM;AAC9D,MAAI;AACJ,QAAM,QAAoB,CAAA;AAC1B,aAAW,CAAC,OAAO,IAAI,KAAK,MAAM,WAAW;AACzC,QAAI,CAAC,KAAK,KAAK,IAAI,GAAG;AAClB,UAAI,QAAQ,EAAG,QAAO;AAEtB,cAAQ,KAAK,KAAA,EAAO,QAAQ,cAAc,EAAE,EAAE,QAAQ,MAAM,EAAE,KAAK;AACnE;AAAA,IACJ;AACA,UAAM,OAAO,SAAS,MAAM,CAAC;AAC7B,QAAI,CAAC,KAAM;AACX,UAAM,SAAS,MAAM,MAAM,SAAS,CAAC;AAErC,QAAI,iBAAiB,KAAK,IAAI,KAAK,OAAQ,EAAC,OAAO,aAAa,CAAA,GAAI,KAAK,IAAI;AAAA,QACxE,OAAM,KAAK,IAAI;AAAA,EACxB;AACA,SAAO,MAAM,SAAS,EAAE,GAAI,QAAQ,EAAE,UAAU,CAAA,GAAK,MAAA,IAAU;AACnE;AAGA,SAAS,SAAS,KAAc,OAAe,QAAsC;AACjF,MAAI,OAAO,QAAQ,YAAY,OAAO,QAAQ,UAAU;AACpD,UAAM,OAAO,SAAS,OAAO,GAAG,CAAC;AACjC,WAAO,OAAO,EAAE,OAAO,KAAK,OAAO,QAAQ,KAAK,UAAU,UAAU,UAAA,IAAc;AAAA,EACtF;AACA,MAAI,MAAM,QAAQ,GAAG,GAAG;AAIpB,WAAO,SAAS,IAAI,CAAC,GAAG,OAAO,MAAM;AAAA,EACzC;AACA,MAAI,CAAC,SAAS,GAAG,EAAG,QAAO;AAC3B,QAAM,IAAI;AAEV,QAAM,WAAW,WAAW,KAAK,CAAC,QAAQ,OAAO,EAAE,GAAG,MAAM,YAAa,EAAE,GAAG,EAAa,MAAM;AACjG,MAAI,QAAQ,WAAY,EAAE,QAAQ,EAAa,SAAS;AACxD,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,UAAU,UAAU,IAAI,SAAS,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,KAAK,MAAM,OAAO,IAAI;AAClF,QAAM,WAAW,UAAU,QAAQ,IAAI,CAAC,SAAS,SAAS,MAAM,CAAC,CAAC,EAAE,OAAO,OAAO,IAAI,CAAA;AAEtF,QAAM,YAAY,OAAO,KAAK,GAAG,UAAU,CAAC,KAAK;AACjD,QAAM,aAAa,OAAO,KAAK,GAAG,QAAQ,CAAC,KAAK;AAChD,QAAM,SAAS,SAAS,CAAC,KAAK,WACtB,SAAS,SAAS,aAAa,QAAQ,IAAI,aAAa,SAAS,YAAY,gBAAgB;AAIrG,QAAM,SAAS,IAAI,KAAK,GAAG,gBAAgB,CAAC;AAC5C,MAAI;AACJ,MAAI,UAAU,WAAW,OAAO;AAC5B,QAAI,WAAW,cAAe,EAAC,OAAO,QAAQ,IAAI,CAAC,QAAQ,KAAK;AAAA,QAC3D,YAAW;AAAA,EACpB;AAEA,QAAM,QAAQ,IAAI,EAAE,OAAO,CAAC;AAC5B,QAAM,UAAU,WAAW,WAAW,QAAQ,WACvC,IAAI,KAAK,GAAG,YAAY,OAAO,CAAC,QAAQ,QAAQ,QAAQ,CAAC,CAAC;AAEjE,QAAM,UAAU,IAAI,KAAK,GAAG,YAAY,CAAC;AACzC,QAAM,KAAK,IAAI,KAAK,GAAG,OAAO,CAAC;AAC/B,MAAI,WAAW,YAAY,OAAO,SAAY,KAAK,MAAO;AAC1D,MAAI,aAAa,UAAa,aAAa,cAAc,cAAc,UAAW,aAAY,WAAW,QAAA,IAAY,UAAU,QAAA,KAAa;AAC5I,MAAI,aAAa,UAAa,WAAW,EAAG,YAAW;AACvD,QAAM,KAAK,IAAI,KAAK,GAAG,CAAC,MAAM,OAAO,MAAM,CAAC,CAAC;AAE7C,SAAO;AAAA,IACH;AAAA,IACA,GAAI,WAAW,EAAE,SAAA,IAAa,CAAA;AAAA,IAC9B;AAAA,IACA,GAAI,SAAS,EAAE,OAAA,IAAW,CAAA;AAAA,IAC1B,GAAI,YAAY,EAAE,UAAA,IAAc,CAAA;AAAA,IAChC,GAAI,aAAa,EAAE,WAAA,IAAe,CAAA;AAAA,IAClC,GAAI,aAAa,SAAY,EAAE,SAAA,IAAa,CAAA;AAAA,IAC5C,GAAI,SAAS,SAAS,EAAE,SAAA,IAAa,CAAA;AAAA,IACrC,GAAI,KAAK,EAAE,OAAO,CAAA;AAAA,EAAC;AAE3B;AAGA,SAAS,IAAI,OAA+B;AACxC,MAAI,OAAO;AACX,QAAM,OAAmB,CAAA;AACzB,aAAW,QAAQ,OAAO;AACtB,QAAI,QAAQ,EAAG;AACf,YAAQ;AACR,QAAI,CAAC,KAAK,UAAU;AAChB,WAAK,KAAK,IAAI;AACd;AAAA,IACJ;AACA,UAAM,WAAW,KAAK,SAAS,MAAM,GAAG,IAAI;AAC5C,YAAQ,SAAS;AACjB,UAAM,OAAiB,EAAE,GAAG,MAAM,SAAA;AAClC,QAAI,CAAC,SAAS,OAAQ,QAAO,KAAK;AAClC,SAAK,KAAK,IAAI;AAAA,EAClB;AACA,SAAO;AACX;AAOO,SAAS,SAAS,KAA+B;AACpD,MAAI,OAAO,QAAQ,UAAU;AACzB,UAAM,SAAS,UAAU,GAAG;AAC5B,WAAO,SAAS,EAAE,GAAI,OAAO,QAAQ,EAAE,OAAO,OAAO,MAAA,IAAU,CAAA,GAAK,OAAO,IAAI,OAAO,KAAK,MAAM;AAAA,EACrG;AACA,MAAI,MAAM,QAAQ,GAAG,GAAG;AACpB,UAAMA,SAAQ,IAAI,IAAI,IAAI,CAAC,SAAS,SAAS,MAAM,CAAC,CAAC,EAAE,OAAO,OAAO,CAAC;AACtE,WAAOA,OAAM,SAAS,EAAE,OAAAA,WAAU;AAAA,EACtC;AACA,MAAI,CAAC,SAAS,GAAG,EAAG,QAAO;AAC3B,QAAM,IAAI;AACV,QAAM,QAAQ,IAAI,KAAK,GAAG,eAAe,CAAC;AAC1C,QAAM,OAAO,IAAI,KAAK,GAAG,SAAS,CAAC;AACnC,QAAM,YAAY,OAAO,KAAK,GAAG,YAAY,CAAC,KAAK;AACnD,QAAM,OAAO,EAAE,GAAI,QAAQ,EAAE,MAAA,IAAU,CAAA,GAAK,GAAI,OAAO,EAAE,KAAA,IAAS,CAAA,GAAK,GAAI,YAAY,EAAE,UAAA,IAAc,IAAK,GAAI,EAAE,OAAO,MAAM,OAAO,EAAE,OAAO,KAAA,IAAS,CAAA,EAAC;AAGzJ,MAAI,SAAS,EAAE,MAAM,CAAC,GAAG;AACrB,UAAM,QAAQ,SAAS,EAAE,MAAM,CAAC;AAChC,WAAO,QAAQ,EAAE,GAAG,MAAM,GAAG,UAAU;AAAA,EAC3C;AAEA,QAAM,OAAO,UAAU,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,KAAK,MAAM,OAAO,KAAK,CAAA;AACnE,QAAM,OAAO,UAAU,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,UAAU,MAAM,QAAQ,KAAK,KAAK,OAAO,UAAU,QAAQ;AAC7G,QAAM,QAAQ,MAAM,QAAQ,IAAI,IAAI,KAAK,IAAI,CAAC,SAAS,SAAS,MAAM,CAAC,CAAC,EAAE,OAAO,OAAO,IAClF,OAAO,SAAS,WAAW,UAAU,IAAI,GAAG,SAAS,CAAA,IACrD,CAAA;AACN,QAAM,QAAQ,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,SAAS,SAAS,MAAM,GAAG,MAAM,CAAC,EAAE,OAAO,OAAO,GAAG,GAAG,KAAK,CAAC;AAC9F,SAAO,MAAM,SAAS,EAAE,GAAG,MAAM,UAAU;AAC/C;AAIA,MAAM,WAAW,CAAC,SAAmB,KAAK,MAAM,QAAQ,CAAC,SAAS,CAAC,MAAM,GAAI,KAAK,YAAY,CAAA,CAAG,CAAC;AAGlG,SAAS,UAAU,MAAoC;AACnD,QAAM,QAAQ,SAAS,IAAI;AAC3B,QAAM,SAAS,MAAM,IAAI,CAAC,MAAM,EAAE,WAAW,QAAA,CAAS,EAAE,OAAO,OAAO;AACtE,MAAI,OAAO,QAAQ;AACf,UAAM,OAAO,MAAM,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,QAAA,GAAW,EAAE,YAAY,QAAA,CAAS,CAAC,EAAE,OAAO,OAAO;AACnG,QAAI,KAAK,UAAW,MAAK,KAAK,KAAK,UAAU,SAAS;AACtD,UAAM,QAAQ,KAAK,IAAI,GAAG,IAAI,IAAI,KAAK,IAAI,GAAG,MAAM,KAAK;AACzD,QAAI,OAAO,EAAG,QAAO;AAAA,EACzB;AACA,QAAM,MAAM,KAAK,MAAM,OAAO,CAAC,OAAO,MAAM,SAAS,EAAE,aAAa,EAAE,YAAY,CAAA,GAAI,OAAO,CAAC,GAAG,QAAQ,KAAK,IAAI,YAAY,IAAI,CAAC,IAAI,CAAC;AACxI,SAAO,MAAM,IAAI,MAAM;AAC3B;AAGO,SAAS,aAAa,MAA8B;AACvD,QAAM,QAAQ,KAAK;AACnB,QAAM,QAAQ,CAAC,WAAuB,MAAM,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM,EAAE;AAC/E,QAAM,QAAQ,MAAM;AACpB,QAAM,OAAO,MAAM,MAAM;AACzB,QAAM,UAAU,MAAM,SAAS;AAC/B,QAAM,WAAW,OAAO;AACxB,MAAI,eAAe,MAAM,UAAU,CAAC,MAAM,EAAE,WAAW,aAAa;AACpE,MAAI,eAAe,EAAG,gBAAe,MAAM,UAAU,CAAC,MAAM,EAAE,WAAW,SAAS;AAClF,MAAI,eAAe,EAAG,gBAAe,MAAM,UAAU,CAAC,MAAM,EAAE,WAAW,SAAS;AAClF,QAAM,eAAe,eAAe,IAAI,MAAM,UAAU,CAAC,MAAM,EAAE,WAAW,QAAQ,IAAI;AACxF,QAAM,UAAU,UAAU,IAAI;AAC9B,SAAO;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,MAAM,QAAQ;AAAA,IACtB,SAAS,MAAM,SAAS;AAAA,IACxB;AAAA,IACA,OAAO,QAAQ,WAAW,QAAQ;AAAA,IAClC,GAAI,gBAAgB,IAAI,EAAE,SAAS,MAAM,YAAY,GAAI,aAAA,IAAiB,CAAA;AAAA,IAC1E,GAAI,gBAAgB,IAAI,EAAE,aAAA,IAAiB,CAAA;AAAA,IAC3C,GAAI,YAAY,SAAY,EAAE,YAAY,CAAA;AAAA,EAAC;AAEnD;AAGA,MAAM,UAAU,CAAC,SAAmB,KAAK,UAAU,QAAQ,SAAS,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,MAAS;AAKtI,SAAS,aAAa,QAAsB,SAAyB;AACxE,QAAM,MAAM,QAAQ,MAAM;AAC1B,QAAM,OAAO,CAAC,OAAe,MAAc,SAAS,MAChD,IAAI,KAAK,aAAa,KAAK,EAAE,OAAO,QAAQ,MAAM,MAAM,aAAa,UAAU,uBAAuB,OAAA,CAAQ,EAAE,OAAO,KAAK;AAChI,QAAM,IAAI,KAAK,IAAI,GAAG,OAAO;AAC7B,MAAI,IAAI,MAAM,CAAC,OAAO,UAAU,CAAC,EAAG,QAAO,KAAK,GAAG,UAAU,CAAC;AAC9D,MAAI,OAAO,KAAK,MAAM,CAAC;AACvB,MAAI,OAAO,GAAI,QAAO,KAAK,MAAM,QAAQ;AACzC,QAAM,QAAkB,CAAA;AACxB,MAAI,QAAQ;AACZ,aAAW,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,OAAO,KAAM,GAAG,CAAC,QAAQ,IAAK,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC,GAAY;AACnG,UAAMC,KAAI,KAAK,MAAM,OAAO,IAAI;AAChC,YAAQA,KAAI;AACZ,QAAIA,KAAI,EAAG,OAAM,KAAK,KAAKA,IAAG,IAAI,CAAC;AACnC,QAAIA,KAAI,KAAK,MAAO,UAAS;AAC7B,QAAI,UAAU,EAAG;AAAA,EACrB;AACA,MAAI;AACA,WAAO,IAAI,KAAK,WAAW,KAAK,EAAE,MAAM,QAAQ,OAAO,SAAA,CAAU,EAAE,OAAO,KAAK;AAAA,EACnF,QAAQ;AACJ,WAAO,MAAM,KAAK,GAAG;AAAA,EACzB;AACJ;AAGA,MAAM,cAAc,CAAC,YAAoB,KAAK,KAAK,MAAM,UAAU,GAAI,IAAI,GAAI;AAG/E,SAAS,UAAU,QAAsB,MAAY,SAA0B;AAC3E,SAAO,IAAI,KAAK,eAAe,QAAQ,MAAM,GAAG,EAAE,MAAM,WAAW,QAAQ,WAAW,GAAI,UAAU,EAAE,QAAQ,UAAA,IAAc,CAAA,GAAK,EAAE,OAAO,IAAI;AAClJ;AAGA,SAAS,aAAa,MAAyB;AAC3C,QAAM,QAAQ,SAAS,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,QAAA,GAAW,EAAE,YAAY,QAAA,CAAS,CAAC,EAAE,OAAO,OAAO;AAC7G,SAAO,CAAC,MAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI;AACtE;AAIA,MAAM,IAAI,CAAC,QAAsB,UAAkB,aAAa,QAAQ,KAAK;AAC7E,MAAM,YAAY,CAAC,MAAc,UAAkB,GAAG,QAAS,OAAO,QAAS,MAAM,CAAC;AACtF,MAAM,WAAW,oBAAI,IAAgB,CAAC,QAAQ,UAAU,SAAS,CAAC;AAElE,SAAS,YAAY,QAAoB,YAAY,sBAAmC;AACpF,SAAO,MAAM,cAAc,MAAM,GAAG,2BAA2B,SAAS,EAAE;AAC9E;AASA,SAAS,QAAQ,MAA6B;AAC1C,SAAO;AAAA,IAAG;AAAA,IAAQ,EAAE,OAAO,2BAA2B,OAAO,EAAE,aAAa,KAAK,OAAO,YAAY,KAAK,WAAS;AAAA,IAC9G,GAAG,QAAQ,EAAE,OAAO,gCAAgC,MAAM,KAAK,OAAO;AAAA,EAAA;AAC9E;AAGA,SAAS,WAAW,KAAkB,MAA6B;AAC/D,QAAM,OAAO,GAAG,QAAQ,EAAE,OAAO,sCAAsC;AACvE,MAAI,KAAK,MAAM,MAAM,KAAK,IAAI,IAAI,QAAQ,YAAY,KAAK,MAAM,CAAC,CAAC,GAAG;AACtE,SAAO;AACX;AAEA,MAAM,WAAW,CAAC,SAAoB,KAAK,SAAS,GAAG,OAAO,EAAE,OAAO,uBAAuB,MAAM,KAAK,OAAA,CAAQ,IAAI;AAQrH,SAAS,eAAe,QAA8B;AAClD,SAAO,CAAC,MAAO,OAAQ,MAAO,EAAE,IAAI,CAAC,MAAM,aAAa,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,MAAO,EAAE,SAAS,EAAE,SAAS,IAAI,CAAE;AACtH;AAGA,SAAS,WAAW,KAAkB,MAA6B;AAC/D,QAAM,QAAQ,KAAK,aAAa,UAAa,SAAS,IAAI,KAAK,MAAM,IAAI,KAAK,WAAW;AACzF,QAAM,OAAO,GAAG,QAAQ,EAAE,OAAO,yBAAyB,OAAO,EAAE,UAAU,UAAU,SAAY,YAAY,KAAK,IAAI,OAAA,GAAa;AACrI,MAAI,UAAU,OAAW,KAAI,KAAK,MAAM,MAAM,aAAa,IAAI,QAAQ,KAAK,CAAC;AAC7E,MAAI,KAAK,MAAM,MAAM,eAAe,IAAI,MAAM,GAAG,cAAc;AAC/D,SAAO;AACX;AAGA,SAAS,KAAK,KAAkB,MAAgB,UAAwB,KAA2B;AAC/F,QAAM,UAAU,KAAK,SAAS,KAAK;AACnC,QAAM,QAAQ,GAAG,QAAQ,EAAE,OAAO,sBAAsB;AACxD,MAAI,KAAK,OAAO,MAAM,IAAI,IAAI,QAAQ,qBAAqB,EAAE,MAAM,EAAE,IAAI,QAAQ,SAAS,IAAI,GAAG,OAAO,EAAE,IAAI,QAAQ,SAAS,KAAK,EAAA,CAAG,CAAC;AACxI,QAAM,OAAO,CAAC,MAA4B,KAAc,UAAkB;AACtE,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,OAAO,GAAG,QAAQ,EAAE,OAAO,qBAAqB,OAAO,EAAE,aAAa,KAAA,GAAQ;AACpF,QAAI,KAAK,MAAM,MAAM,IAAI,IAAI,QAAQ,KAAK,EAAE,GAAG,EAAE,IAAI,QAAQ,KAAK,EAAA,CAAG,CAAC;AACtE,WAAO;AAAA,EACX;AACA,MAAI,UAA8B;AAClC,MAAI,SAAS,YAAY,QAAW;AAChC,UAAM,UAAU,SAAS;AACzB,UAAM,OAAO,GAAG,QAAQ,EAAE,OAAO,6BAA6B;AAC9D,QAAI,KAAK,MAAM,MAAM,IAAI,IAAI,QAAQ,oBAAoB,EAAE,MAAM,aAAa,IAAI,QAAQ,OAAO,EAAA,CAAG,CAAC;AACrG,cAAU,GAAG,QAAQ,EAAE,OAAO,0BAA0B,MAAM,OAAO,4CAA4C,GAAG,IAAI;AAAA,EAC5H;AACA,MAAI,QAA4B;AAChC,MAAI,KAAK;AACL,YAAQ;AAAA,MAAG;AAAA,MAAO,EAAE,OAAO,oBAAoB,OAAO,EAAE,eAAe,QAAQ,iBAAiB,SAAS,QAAQ,KAAK,SAAS,aAAa,SAAS,QAAM;AAAA,MACvJ,GAAG,QAAQ,EAAE,OAAO,yBAAyB;AAAA,MAC7C,GAAG,QAAQ,EAAE,OAAO,4BAA4B;AAAA,IAAA;AACpD,UAAM,MAAM,YAAY,4BAA4B,UAAU,SAAS,MAAM,SAAS,KAAK,CAAC;AAC5F,UAAM,MAAM,YAAY,+BAA+B,UAAU,SAAS,SAAS,SAAS,KAAK,CAAC;AAAA,EACtG;AACA,SAAO;AAAA,IAAG;AAAA,IAAO,EAAE,OAAO,oBAAA;AAAA,IACtB,UAAU;AAAA,MAAG;AAAA,MAAO,EAAE,OAAO,uBAAA;AAAA,MACzB,GAAG,OAAO,EAAE,OAAO,sBAAsB,MAAM,SAAS;AAAA,MACxD,KAAK,SAAS,KAAK,OAAO,GAAG,OAAO,EAAE,OAAO,qBAAqB,MAAM,KAAK,KAAA,CAAM,IAAI;AAAA,IAAA,IAAQ;AAAA,IACnG;AAAA,MAAG;AAAA,MAAO,EAAE,OAAO,qBAAA;AAAA,MACf;AAAA,MACA,KAAK,WAAW,oBAAoB,SAAS,OAAO;AAAA,MACpD,KAAK,UAAU,mBAAmB,SAAS,MAAM;AAAA,MACjD;AAAA,IAAA;AAAA,IACJ;AAAA,EAAA;AACR;AAGA,SAAS,SAAS,KAAkB,MAAgB,EAAE,OAAO,UAAU,OAAO,MAAM,SAA8E;AAC9J,QAAM,MAAM;AAAA,IAAG;AAAA,IAAO,EAAE,OAAO,mBAAA;AAAA,IAC3B,YAAY,KAAK,MAAM;AAAA,IACvB,GAAG,OAAO,EAAE,OAAO,uBAAuB,QAAQ,IAAI,GAAG,WAAW,KAAK,IAAI,GAAG,SAAS,IAAI,CAAC;AAAA,IAC9F,QAAQ,WAAW,KAAK,IAAI,IAAI;AAAA,EAAA;AACpC,QAAM,OAAO,GAAG,MAAM;AAAA,IAClB,OAAO,oBAAoB,MAAM,4BAA4B,EAAE;AAAA,IAC/D,OAAO,EAAE,eAAe,KAAK,QAAQ,gBAAgB,UAAU,SAAS,QAAW,gBAAgB,KAAK,GAAA;AAAA,EAAG,GAC5G,GAAG;AACN,MAAI,KAAK,UAAU,QAAQ;AACvB,SAAK,OAAO;AAAA,MAAG;AAAA,MAAM,EAAE,OAAO,yBAAyB,OAAO,EAAE,MAAM,SAAO;AAAA,MACzE,GAAG,KAAK,SAAS,IAAI,CAAC,MAAM,SAAS,KAAK,GAAG,EAAE,OAAO,KAAK,KAAA,CAAM,CAAC;AAAA,IAAA,CAAE;AAAA,EAC5E;AACA,SAAO;AACX;AAGA,SAAS,UAAU,KAAkB,MAAgB,UAAgC;AACjF,QAAM,QAAQ,QAAQ,IAAI;AAC1B,QAAM,OAAO;AAAA,IAAG;AAAA,IAAM,EAAE,OAAO,sBAAsB,OAAO,EAAE,MAAM,QAAQ,cAAc,QAAM;AAAA,IAC5F,GAAG,KAAK,MAAM,IAAI,CAAC,MAAM,MAAM,SAAS,KAAK,MAAM,EAAE,OAAO,SAAS,MAAM,SAAS,cAAc,CAAC;AAAA,EAAA;AACvG,SAAO,CAAC,KAAK,KAAK,MAAM,UAAU,IAAI,GAAG,IAAI;AACjD;AAOA,SAAS,QAAQ,KAAkB,MAAgB,SAA+B;AAC9E,QAAM,UAAU,KAAK;AACrB,QAAM,QAAQ,GAAG,QAAQ,EAAE,OAAO,sBAAsB,OAAO,EAAE,UAAU,SAAS,YAAA,EAAY,GAAK;AACrG,MAAI,QAAS,KAAI,KAAK,OAAO,MAAM,UAAU,IAAI,QAAQ,SAAS,OAAO,CAAC;AAC1E,SAAO,GAAG,OAAO,EAAE,OAAO,uBAAuB,OAAO,WAAW,KAAK,IAAI,CAAC;AACjF;AAGA,SAAS,SAAS,KAAkB,MAAgB,UAAgC;AAChF,QAAM,QAAQ,QAAQ,IAAI;AAC1B,QAAM,UAAU,aAAa,IAAI;AACjC,QAAM,OAAO,GAAG,MAAM,EAAE,OAAO,yBAAyB,OAAO,EAAE,MAAM,QAAQ,cAAc,MAAA,GAAS;AACtG,OAAK,MAAM,QAAQ,CAAC,MAAM,MAAM;AAC5B,UAAM,OAAO;AAAA,MAAG;AAAA,MAAQ,EAAE,OAAO,oBAAA;AAAA,MAC7B,MAAM,KAAK,WAAW,gBAAgB,eAAe,cAAc,KAAK,MAAM,GAAG,iDAAiD;AAAA,IAAA;AACtI,UAAM,OAAO;AAAA,MAAG;AAAA,MAAO,EAAE,OAAO,0BAAA;AAAA,MAC5B,QAAQ,IAAI;AAAA,MACZ,WAAW,KAAK,IAAI;AAAA,MACpB,QAAQ,QAAQ,KAAK,MAAM,OAAO,IAAI;AAAA,MACtC,SAAS,IAAI;AAAA,MACb,KAAK,UAAU,SACT,GAAG,MAAM,EAAE,OAAO,yBAAyB,OAAO,EAAE,MAAM,OAAA,KAAY,GAAG,KAAK,SAAS,IAAI,CAAC,MAAM,SAAS,KAAK,GAAG,EAAE,OAAO,OAAO,KAAK,KAAA,CAAM,CAAC,CAAC,IAChJ;AAAA,IAAA;AACV,SAAK,OAAO,GAAG,MAAM;AAAA,MACjB,OAAO;AAAA,MACP,OAAO,EAAE,eAAe,KAAK,QAAQ,gBAAgB,MAAM,SAAS,eAAe,SAAS,QAAW,gBAAgB,KAAK,GAAA;AAAA,IAAG,GAChI,MAAM,IAAI,CAAC;AAAA,EAClB,CAAC;AACD,SAAO,CAAC,KAAK,KAAK,MAAM,UAAU,KAAK,GAAG,IAAI;AAClD;AAEA,MAAM,MAAM;AAGZ,SAAS,KAAK,UAAqC;AAC/C,QAAMC,OAAM,SAAS,gBAAgB,KAAK,KAAK;AAC/C,EAAAA,KAAI,aAAa,WAAW,WAAW;AACvC,EAAAA,KAAI,aAAa,SAAS,uBAAuB;AACjD,QAAM,MAAM,CAAC,KAAa,OAAgB,SAAS,MAAM;AACrD,UAAM,SAAS,SAAS,gBAAgB,KAAK,QAAQ;AACrD,eAAW,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC,cAAc,KAAK,GAAG,CAAC,SAAS,GAAG,CAAC,EAAG,QAAO,aAAa,MAAO,KAAM;AAChJ,QAAI,UAAU,QAAW;AACrB,aAAO,aAAa,oBAAoB,GAAG,KAAK,MAAM;AACtD,aAAO,aAAa,qBAAqB,OAAO,CAAC,MAAM,CAAC;AAAA,IAC5D;AACA,WAAO;AAAA,EACX;AACA,QAAM,OAAO,SAAS,QAAS,SAAS,OAAO,SAAS,QAAS,MAAM;AACvE,QAAM,UAAU,SAAS,QAAS,SAAS,UAAU,SAAS,QAAS,MAAM;AAC7E,EAAAA,KAAI,OAAO,IAAI,yBAAyB,GAAG,IAAI,0BAA0B,IAAI,GAAG,IAAI,6BAA6B,SAAS,IAAI,CAAC;AAC/H,SAAO,GAAG,QAAQ,EAAE,OAAO,qBAAqB,OAAO,EAAE,eAAe,QAAQ,iBAAiB,SAAS,QAAQ,KAAK,SAAS,aAAa,SAAS,MAAA,EAAM,GAAKA,IAAG;AACxK;AAMA,SAAS,QAAQ,KAAkB,MAAgB,UAAgC;AAC/E,QAAM,QAAQ,SAAS,gBAAgB,SAAS;AAChD,QAAM,QAAQ,UAAU,SAAY,KAAK,MAAM,KAAK,IAAI;AACxD,QAAM,OAAO,GAAG,QAAQ,EAAE,OAAO,6BAA6B;AAC9D,QAAM,QAAuB,CAAA;AAC7B,MAAI,OAAO;AACP,QAAI,KAAK,MAAM,MAAM,IAAI,IAAI,QAAQ,oBAAoB,EAAE,GAAG,EAAE,IAAI,QAAQ,QAAS,CAAC,GAAG,OAAO,EAAE,IAAI,QAAQ,SAAS,KAAK,EAAA,CAAG,CAAC;AAChI,UAAM,KAAK,MAAM,GAAG,QAAQ,EAAE,OAAO,8BAA8B,MAAM,MAAM,MAAA,CAAO,CAAC;AAAA,EAC3F,OAAO;AACH,QAAI,KAAK,MAAM,oBAAoB,EAAE,gBAAgB;AACrD,UAAM,OAAO,KAAK,SAAS,KAAK;AAChC,QAAI,KAAM,OAAM,KAAK,GAAG,QAAQ,EAAE,OAAO,8BAA8B,MAAM,KAAA,CAAM,CAAC;AACpF,UAAM,KAAK,IAAI;AAAA,EACnB;AACA,QAAM,UAAU,GAAG,QAAQ,EAAE,OAAO,wBAAwB;AAC5D,MAAI,KAAK,SAAS,MAAM,cAAc,IAAI,QAAQ,SAAS,KAAK,CAAC;AACjE,QAAM,SAAqB,QAAQ,MAAM,SAAS;AAClD,SAAO,CAAC;AAAA,IAAG;AAAA,IAAO,EAAE,OAAO,wBAAwB,OAAO,EAAE,eAAe,SAAO;AAAA,IAC9E,YAAY,QAAQ,+CAA+C;AAAA,IACnE,GAAG,QAAQ,EAAE,OAAO,4BAAA,GAA+B,GAAG,KAAK;AAAA,IAC3D,KAAK,QAAQ;AAAA,IACb;AAAA,EAAA,CAAQ;AAChB;AAEA,SAAS,OAAO,MAAgB,KAAuC;AACnE,QAAM,WAAW,aAAa,IAAI;AAClC,MAAI,IAAI,YAAY,mBAAmB,SAAS,KAAK,MAAM,QAAQ;AACnE,MAAI,IAAI,YAAY,kBAAkB,QAAQ,KAAK,MAAM,QAAQ;AACjE,SAAO,UAAU,KAAK,MAAM,QAAQ;AACxC;AAEA,SAAS,SAAS,SAAmC,KAAuC;AAExF,MAAI,SAAS,OAAO,OAAQ,QAAO,OAAO,SAAqB,GAAG;AAClE,QAAM,QAAQ,CAAC,KAAa,OAAmC,WAC3D,GAAG,QAAQ,EAAE,OAAO,oCAAoC,IAAI,IAAI,GAAG,IAAI,OAAO,EAAE,eAAe,OAAA,GAAU;AAC7G,QAAM,UAAU,SAAS,SAAS,SAAS;AAC3C,MAAI,IAAI,YAAY,WAAW;AAC3B,WAAO,CAAC;AAAA,MAAG;AAAA,MAAO,EAAE,OAAO,uBAAA;AAAA,MACvB,MAAM,yBAAyB,QAAQ;AAAA,MACvC;AAAA,QAAG;AAAA,QAAQ,EAAE,OAAO,4BAAA;AAAA,QAChB,UAAU,GAAG,QAAQ,EAAE,OAAO,8BAA8B,MAAM,QAAA,CAAS,IAAI,MAAM,wBAAwB,MAAM;AAAA,MAAA;AAAA,MACvH,MAAM,wBAAwB,QAAQ;AAAA,IAAA,CAAE;AAAA,EAChD;AACA,QAAM,MAAM;AAAA,IAAG;AAAA,IAAO,EAAE,OAAO,oBAAA;AAAA,IAC3B,UAAU,GAAG,OAAO,EAAE,OAAO,uBAAA,GAA0B,GAAG,OAAO,EAAE,OAAO,sBAAsB,MAAM,QAAA,CAAS,CAAC,IAAI,MAAM,yBAAyB,MAAM;AAAA,IACzJ,GAAG,OAAO,EAAE,OAAO,wBAAwB,MAAM,yBAAyB,MAAM,CAAC;AAAA,IACjF,IAAI,YAAY,cAAc,GAAG,OAAO,EAAE,OAAO,oBAAoB,OAAO,EAAE,eAAe,OAAA,EAAO,CAAG,IAAI;AAAA,EAAA;AAC/G,QAAM,OAAO,CAAC,GAAG,GAAG,GAAG,CAAC;AACxB,MAAI,IAAI,YAAY,YAAY;AAC5B,WAAO,CAAC,KAAK,GAAG,MAAM,EAAE,OAAO,yBAAyB,OAAO,EAAE,eAAe,SAAO,GAAK,GAAG,KAAK,IAAI,MACpG;AAAA,MAAG;AAAA,MAAM,EAAE,OAAO,sBAAsB,OAAO,EAAE,eAAe,YAAU;AAAA,MACtE,GAAG,QAAQ,EAAE,OAAO,uBAAuB,MAAM,wBAAwB,QAAQ,CAAC;AAAA,MAClF,GAAG,OAAO,EAAE,OAAO,0BAAA,GAA6B,MAAM,wBAAwB,MAAM,GAAG,MAAM,wBAAwB,MAAM,CAAC;AAAA,IAAA,CAAE,CAAC,CAAC;AAAA,EAC5I;AACA,SAAO,CAAC,KAAK,GAAG,MAAM,EAAE,OAAO,sBAAsB,OAAO,EAAE,eAAe,SAAO,GAAK,GAAG,KAAK,IAAI,MACjG;AAAA,IAAG;AAAA,IAAM,EAAE,OAAO,oBAAA;AAAA,IACd,GAAG,OAAO,EAAE,OAAO,mBAAA,GAAsB,MAAM,yBAAyB,QAAQ,GAAG,MAAM,wBAAwB,MAAM,CAAC;AAAA,EAAA,CAAE,CAAC,CAAC;AACxI;AAEA,SAAS,UAAU,MAAgC,KAA0B;AACzE,SAAO,MAAM,SAAS,MAAM,QAAQ,IAAI,IAAI,QAAQ,gBAAgB;AACxE;AAGA,MAAM,OAAmC,EAAE,MAAM,OAAO,SAAS,OAAO,eAAe,OAAO,QAAQ,OAAO,SAAS,OAAO,SAAS,MAAA;AAGtI,MAAM,OAAO,CAAC,SAAkB,KAAK,SAAS,MAAM,GAAG,KAAK,MAAM,GAAG,GAAG,EAAE,QAAA,CAAS,MAAM;AAMzF,SAAS,SAAS,MAAgB,KAA0B;AACxD,QAAM,WAAW,aAAa,IAAI;AAClC,QAAM,UAAU,IAAI,IAAI,QAAQ,oBAAoB,EAAE,MAAM,EAAE,IAAI,QAAQ,SAAS,IAAI,GAAG,OAAO,EAAE,IAAI,QAAQ,SAAS,KAAK,GAAG;AAChI,QAAM,MAAM,SAAS,UAAU,MAAM,IAAI,IAAI,QAAQ,gBAAgB,EAAE,MAAM,SAAS,QAAQ,MAAA,CAAO,CAAC,KAAK;AAC3G,QAAM,OAAO,CAAC,MAAgB,WAAmB;AAC7C,UAAM,OAAO,KAAK,WAAW,YAAY,KAAK,WAAW,cAAc,KAAK,SAAS,MAAM,KAAK,KAAK,MAAM,CAAC,KAAK;AACjH,WAAO,GAAG,MAAM,KAAK,KAAK,KAAK,MAAM,CAAC,IAAI,KAAK,KAAK,GAAG,GAAG;AAAA,EAC9D;AACA,QAAM,QAAQ,KAAK,MAAM,QAAQ,CAAC,SAAS,CAAC,KAAK,MAAM,EAAE,GAAG,IAAI,KAAK,YAAY,CAAA,GAAI,IAAI,CAAC,QAAQ,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC;AACnH,SAAO,CAAC,GAAG,UAAU,MAAM,GAAG,CAAC,KAAK,OAAO,GAAG,GAAG,IAAI,GAAG,KAAK,EAAE,KAAK,IAAI;AAC5E;AAEO,MAAM,WAAW,WAAkC;AAAA,EACtD,MAAM;AAAA,EACN,UAAU,CAAC,aAAa,YAAY,SAAS;AAAA,EAC7C,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA,SAAS,CAAC,UAAU;AAChB,UAAM,OAAO,SAAS,KAAK;AAC3B,QAAI,KAAM,QAAO;AACjB,UAAM,QAAQ,IAAI,MAAM,OAAO,KAAK,MAAM,MAAM,KAAK,MAAM,MAAM,KAAK,MAAM,WAAW,KAAK,MAAM,OAAO,CAAC;AAC1G,WAAO,QAAQ,EAAE,OAAO,MAAA,IAAU;AAAA,EACtC;AACJ,CAAC;AAGM,SAAS,cAAc,UAAmD,IAAgB;AAC7F,SAAO,SAAS,MAAM,OAAO;AACjC;"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The product card's glyphs: the bag a tile shows when it has no photo, the van beside a
|
|
3
|
+
* delivery line, the loupe before a search that is still running.
|
|
4
|
+
*
|
|
5
|
+
* Drawn on a 24 × 24 grid in `currentColor` strokes, so the sheet colours them through
|
|
6
|
+
* `color` and a theme follows. Constants of this package — the only markup the card parses.
|
|
7
|
+
*/
|
|
8
|
+
/** A shopping bag: the placeholder where a photo is missing or failed to load. */
|
|
9
|
+
export declare const BAG: string;
|
|
10
|
+
/** A delivery van, before the shipping line. */
|
|
11
|
+
export declare const TRUCK: string;
|
|
12
|
+
/** A loupe, before the query a running search was given. */
|
|
13
|
+
export declare const SEARCH: string;
|
|
14
|
+
/** The card's own glyph, for its one-line states (waiting, refused, failed). */
|
|
15
|
+
export declare const TAG: string;
|
|
16
|
+
//# sourceMappingURL=icons.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../../src/product/icons.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,kFAAkF;AAClF,eAAO,MAAM,GAAG,QAAiI,CAAC;AAElJ,gDAAgD;AAChD,eAAO,MAAM,KAAK,QAAyJ,CAAC;AAE5K,4DAA4D;AAC5D,eAAO,MAAM,MAAM,QAA6E,CAAC;AAEjG,gFAAgF;AAChF,eAAO,MAAM,GAAG,QAAkJ,CAAC"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @aparte/plugin-cards/product — a tool's products, drawn as cards.
|
|
3
|
+
*
|
|
4
|
+
* import { setupProductCard } from '@aparte/plugin-cards/product';
|
|
5
|
+
*
|
|
6
|
+
* setupProductCard({
|
|
7
|
+
* tool: 'search_products',
|
|
8
|
+
* map: (result) => ({ title: result.query, products: result.items }),
|
|
9
|
+
* });
|
|
10
|
+
*
|
|
11
|
+
* Three variants of one card: `card` (a tile — the photo, the shop, the name, the stars,
|
|
12
|
+
* the price; several products are a row of tiles that scrolls), `compact` (a row per
|
|
13
|
+
* product, to scan) and `detail` (one product at length, the others in a small row under
|
|
14
|
+
* it). Your tool keeps its own fields: `readProducts` already reads the usual spellings —
|
|
15
|
+
* `name` or `title`, a price as `49.9`, `"49.90"` or `"49,90 € TTC"`, schema.org's `offers`
|
|
16
|
+
* and `aggregateRating`, a Google Shopping result's `extracted_price` and `source`, a Shopify
|
|
17
|
+
* product from the Admin API (`variants`, `body_html`) or the Storefront API (`priceRange`,
|
|
18
|
+
* `onlineStoreUrl`) — and `map` covers the rest.
|
|
19
|
+
*/
|
|
20
|
+
import type { AparteConfig } from '@aparte/core';
|
|
21
|
+
import { type CardAction, type CardSetupOptions } from '../kit/define.js';
|
|
22
|
+
export type ProductVariant = 'card' | 'compact' | 'detail';
|
|
23
|
+
/** Where a product stands; anything else a tool writes is shown as it wrote it. */
|
|
24
|
+
export type ProductAvailability = 'in-stock' | 'low-stock' | 'out-of-stock' | 'preorder';
|
|
25
|
+
/**
|
|
26
|
+
* One product as `map` returns it. Only `title` is needed; every other field adds a line
|
|
27
|
+
* when it is there and nothing when it is not.
|
|
28
|
+
*/
|
|
29
|
+
export interface ProductInput {
|
|
30
|
+
title: string;
|
|
31
|
+
/** An `http(s)` URL, or `{ src, alt }`. Anything else is no photo. */
|
|
32
|
+
image?: string | {
|
|
33
|
+
src: string;
|
|
34
|
+
alt?: string;
|
|
35
|
+
};
|
|
36
|
+
/** `49.9`, or a string a shop writes: `"49.90"`, `"$49.90"`, `"49,90 €"`. */
|
|
37
|
+
price?: number | string;
|
|
38
|
+
/** An ISO 4217 code (`EUR`) or a usual symbol (`€`). */
|
|
39
|
+
currency?: string;
|
|
40
|
+
/** The price before the sale: struck through, and the discount computed from it. */
|
|
41
|
+
compareAtPrice?: number | string;
|
|
42
|
+
/** 0–5. */
|
|
43
|
+
rating?: number;
|
|
44
|
+
reviewCount?: number;
|
|
45
|
+
availability?: ProductAvailability | string;
|
|
46
|
+
merchant?: string;
|
|
47
|
+
/** The shop's logo, small, before its name. */
|
|
48
|
+
merchantLogo?: string;
|
|
49
|
+
/** A word over the photo: "Bestseller", "New". */
|
|
50
|
+
badge?: string;
|
|
51
|
+
/** Sizes, colours: drawn as chips in `detail`. */
|
|
52
|
+
options?: string[];
|
|
53
|
+
/** A line of your own ("Delivered tomorrow"), or a cost — `0` reads "Free delivery". */
|
|
54
|
+
shipping?: string | number;
|
|
55
|
+
/** A few lines, in `detail`. Tags are stripped: it is shown as text. */
|
|
56
|
+
description?: string;
|
|
57
|
+
/** Where the product's name and photo link to; `http(s)` only. */
|
|
58
|
+
url?: string;
|
|
59
|
+
/** Your own id, handed back with the product in `aparte-card-action`. */
|
|
60
|
+
id?: string;
|
|
61
|
+
/**
|
|
62
|
+
* At most two buttons per product, each a link (`http(s)`) or an `id`, reporting the
|
|
63
|
+
* product as its `data`. A `send` is dropped: a result never writes the person's next
|
|
64
|
+
* message — send one from your `aparte-card-action` listener, or the card's `actions`.
|
|
65
|
+
*/
|
|
66
|
+
actions?: CardAction[];
|
|
67
|
+
}
|
|
68
|
+
/** What `map` returns: one product, a list, or a list with a heading. */
|
|
69
|
+
export type ProductsInput = ProductInput | ProductInput[] | {
|
|
70
|
+
title?: string;
|
|
71
|
+
products: ProductInput[];
|
|
72
|
+
};
|
|
73
|
+
/** One product as the card draws it, once read. */
|
|
74
|
+
export interface Product {
|
|
75
|
+
id?: string;
|
|
76
|
+
title: string;
|
|
77
|
+
image?: {
|
|
78
|
+
src: string;
|
|
79
|
+
alt?: string;
|
|
80
|
+
};
|
|
81
|
+
price?: number;
|
|
82
|
+
/** An ISO 4217 code. */
|
|
83
|
+
currency?: string;
|
|
84
|
+
/** Kept only when it is above `price`: a "was" price lower than the price is no sale. */
|
|
85
|
+
compareAtPrice?: number;
|
|
86
|
+
rating?: number;
|
|
87
|
+
reviewCount?: number;
|
|
88
|
+
/** One of the four, or the tool's own words. */
|
|
89
|
+
availability?: {
|
|
90
|
+
status: ProductAvailability;
|
|
91
|
+
} | {
|
|
92
|
+
text: string;
|
|
93
|
+
};
|
|
94
|
+
merchant?: string;
|
|
95
|
+
merchantLogo?: string;
|
|
96
|
+
badge?: string;
|
|
97
|
+
options: string[];
|
|
98
|
+
shipping?: {
|
|
99
|
+
text: string;
|
|
100
|
+
} | {
|
|
101
|
+
cost: number;
|
|
102
|
+
};
|
|
103
|
+
description?: string;
|
|
104
|
+
url?: string;
|
|
105
|
+
actions: CardAction[];
|
|
106
|
+
}
|
|
107
|
+
/** The data as the card draws it. */
|
|
108
|
+
export interface ProductData {
|
|
109
|
+
/** The heading over several products ("Trail shoes under €100"). */
|
|
110
|
+
title?: string;
|
|
111
|
+
products: Product[];
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The card's data from the loose shape `map` returns — one product, a list, or
|
|
115
|
+
* `{ title, products }` (also `results`, `items`, Google Shopping's `shopping_results`) —
|
|
116
|
+
* or `null` when not one product has a name. At most ten are kept.
|
|
117
|
+
*/
|
|
118
|
+
export declare function readProducts(raw: unknown): ProductData | null;
|
|
119
|
+
/** The share taken off, from the two prices — never read from a tool's "-20%". `undefined` under one percent. */
|
|
120
|
+
export declare function discountOf(product: Pick<Product, 'price' | 'compareAtPrice'>): number | undefined;
|
|
121
|
+
export declare const productCard: {
|
|
122
|
+
name: string;
|
|
123
|
+
variants: readonly ProductVariant[];
|
|
124
|
+
setup: (options?: CardSetupOptions<ProductData, ProductVariant>) => () => void;
|
|
125
|
+
segment: (raw: unknown, options?: {
|
|
126
|
+
id?: string;
|
|
127
|
+
variant?: ProductVariant | undefined;
|
|
128
|
+
config?: AparteConfig;
|
|
129
|
+
}) => import("../index.js").AparteCardSegment;
|
|
130
|
+
};
|
|
131
|
+
/** Draw a tool's products as this card — returns the function that stops it. */
|
|
132
|
+
export declare function setupProductCard(options?: CardSetupOptions<ProductData, ProductVariant>): () => void;
|
|
133
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/product/index.ts"],"names":[],"mappings":"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"}
|