executable-stories-react 0.15.0 → 0.16.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/dist/index.js CHANGED
@@ -522,12 +522,29 @@ function DocVideo({ entry }) {
522
522
  }
523
523
 
524
524
  // src/components/doc/DocHtml.tsx
525
+ import { useEffect, useRef, useState } from "react";
525
526
  import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
527
+ var MEASURE_SCRIPT = "<script>(function(){if(parent===window)return;var last=0,queued=false;var measure=function(){var kids=document.body.children,h=0;for(var i=0;i<kids.length;i++)h=Math.max(h,kids[i].getBoundingClientRect().bottom);return Math.ceil(h||document.body.getBoundingClientRect().bottom)};var post=function(){queued=false;var h=measure();if(h===last)return;last=h;parent.postMessage({__esHtmlHeight:h},'*')};var schedule=function(){if(queued)return;queued=true;requestAnimationFrame(post)};var start=function(){new ResizeObserver(schedule).observe(document.body);new MutationObserver(schedule).observe(document.body,{subtree:true,childList:true,attributes:true,characterData:true});addEventListener('load',schedule);post()};if(document.body)start();else addEventListener('DOMContentLoaded',start)})();</script>";
528
+ var MAX_MEASURED_HEIGHT = 5e3;
526
529
  function DocHtml({ entry }) {
527
- const height = typeof entry.height === "number" ? `${entry.height}px` : entry.height ?? "400px";
530
+ const declaredHeight = typeof entry.height === "number" ? `${entry.height}px` : entry.height ?? "400px";
528
531
  const title = entry.title ?? "Embedded HTML";
529
532
  const safeHref = safeUrl(entry.url ?? entry.path);
530
- const frameProps = entry.content !== void 0 ? { srcDoc: entry.content } : { src: safeHref ?? "" };
533
+ const frameRef = useRef(null);
534
+ const [measuredHeight, setMeasuredHeight] = useState();
535
+ useEffect(() => {
536
+ if (entry.content === void 0) return;
537
+ const onMessage = (event) => {
538
+ if (event.source !== frameRef.current?.contentWindow) return;
539
+ const reported = event.data?.__esHtmlHeight;
540
+ if (typeof reported !== "number" || !Number.isFinite(reported) || reported <= 0) return;
541
+ setMeasuredHeight(Math.min(reported, MAX_MEASURED_HEIGHT));
542
+ };
543
+ window.addEventListener("message", onMessage);
544
+ return () => window.removeEventListener("message", onMessage);
545
+ }, [entry.content]);
546
+ const height = measuredHeight === void 0 ? declaredHeight : `${measuredHeight}px`;
547
+ const frameProps = entry.content !== void 0 ? { srcDoc: entry.content + MEASURE_SCRIPT } : { src: safeHref ?? "" };
531
548
  return /* @__PURE__ */ jsxs9("figure", { className: "my-3 overflow-hidden rounded-md border border-border", children: [
532
549
  /* @__PURE__ */ jsxs9("figcaption", { className: "flex items-center justify-between border-b border-border bg-muted/40 px-3 py-1.5 text-xs font-medium text-muted-foreground", children: [
533
550
  /* @__PURE__ */ jsx14("span", { children: entry.title ?? "HTML" }),
@@ -547,6 +564,7 @@ function DocHtml({ entry }) {
547
564
  /* @__PURE__ */ jsx14(
548
565
  "iframe",
549
566
  {
567
+ ref: frameRef,
550
568
  ...frameProps,
551
569
  sandbox: "allow-scripts",
552
570
  loading: "lazy",
@@ -1095,7 +1113,7 @@ function ScenarioRunHistory({ scenarioId }) {
1095
1113
  }
1096
1114
 
1097
1115
  // src/components/ScenarioStaleness.tsx
1098
- import { useEffect, useState } from "react";
1116
+ import { useEffect as useEffect2, useState as useState2 } from "react";
1099
1117
 
1100
1118
  // src/lib/provenance.ts
1101
1119
  function reportLastRunMs(report) {
@@ -1163,8 +1181,8 @@ import { jsxs as jsxs16 } from "react/jsx-runtime";
1163
1181
  function ScenarioStaleness({ scenario }) {
1164
1182
  const freshness = useScenarioFreshness();
1165
1183
  const fixedNow = freshness?.nowMs;
1166
- const [now, setNow] = useState(fixedNow);
1167
- useEffect(() => {
1184
+ const [now, setNow] = useState2(fixedNow);
1185
+ useEffect2(() => {
1168
1186
  if (fixedNow === void 0) setNow(Date.now());
1169
1187
  }, [fixedNow]);
1170
1188
  if (!freshness || now === void 0) return null;
@@ -1178,7 +1196,7 @@ function ScenarioStaleness({ scenario }) {
1178
1196
  }
1179
1197
 
1180
1198
  // src/interactive/collapse-context.ts
1181
- import { createContext as createContext4, useCallback, useContext as useContext5, useState as useState2 } from "react";
1199
+ import { createContext as createContext4, useCallback, useContext as useContext5, useState as useState3 } from "react";
1182
1200
  var CollapseContext = createContext4(null);
1183
1201
  var CollapseProvider = CollapseContext.Provider;
1184
1202
  function useCollapse() {
@@ -1752,25 +1770,86 @@ function ReportErrorShell({
1752
1770
  return /* @__PURE__ */ jsx36("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ jsx36(ReportSchemaError, { error }) });
1753
1771
  }
1754
1772
 
1755
- // src/components/Report.tsx
1773
+ // src/components/ReportSpanGraph.tsx
1774
+ import { deriveSpanGraph, spanGraphNodeMark, spanGraphToMermaid } from "executable-stories-core/span-graph";
1775
+ import { useMemo as useMemo2 } from "react";
1756
1776
  import { jsx as jsx37, jsxs as jsxs25 } from "react/jsx-runtime";
1777
+ var LANE_LABEL = {
1778
+ edge: "Edge",
1779
+ service: "Service",
1780
+ queue: "Queue",
1781
+ datastore: "Data"
1782
+ };
1783
+ var MARK_LABEL = {
1784
+ failing: "Failing",
1785
+ added: "New",
1786
+ changed: "Changed"
1787
+ };
1788
+ var MARK_CLASS = {
1789
+ failing: "text-fail",
1790
+ added: "text-pass",
1791
+ changed: "text-pend"
1792
+ };
1793
+ function MarkBadge({ node }) {
1794
+ const mark = spanGraphNodeMark(node);
1795
+ if (!mark) return null;
1796
+ return /* @__PURE__ */ jsx37("span", { className: cn("ml-2 text-xs font-medium", MARK_CLASS[mark]), children: MARK_LABEL[mark] });
1797
+ }
1798
+ function ReportSpanGraph() {
1799
+ const report = useReport();
1800
+ const { code, graph, titleById } = useMemo2(() => {
1801
+ const scenarios = report.features.flatMap((feature) => feature.scenarios);
1802
+ const derived = deriveSpanGraph(scenarios);
1803
+ return {
1804
+ graph: derived,
1805
+ code: spanGraphToMermaid(derived),
1806
+ titleById: new Map(scenarios.map((scenario) => [scenario.id, scenario.title]))
1807
+ };
1808
+ }, [report]);
1809
+ if (code === "") return null;
1810
+ return /* @__PURE__ */ jsxs25("section", { className: "es-span-graph mt-6", "aria-labelledby": "es-span-graph-heading", children: [
1811
+ /* @__PURE__ */ jsx37("h2", { id: "es-span-graph-heading", className: "text-base font-semibold text-foreground", children: "Architecture, as it ran" }),
1812
+ /* @__PURE__ */ jsx37("p", { className: "mt-1 text-sm text-muted-foreground", children: "Derived from this run\u2019s traces. Every component was named by a span while a scenario ran, so nothing here is inferred from the code. Instrumented, exercised paths only: a component no scenario reaches does not appear." }),
1813
+ /* @__PURE__ */ jsx37(DocMermaid, { entry: { kind: "mermaid", phase: "static", code } }),
1814
+ /* @__PURE__ */ jsxs25("table", { className: "mt-2 w-full text-left text-sm", children: [
1815
+ /* @__PURE__ */ jsx37("caption", { className: "sr-only", children: "Each component and the scenarios that exercised it" }),
1816
+ /* @__PURE__ */ jsx37("thead", { children: /* @__PURE__ */ jsxs25("tr", { className: "text-xs text-muted-foreground", children: [
1817
+ /* @__PURE__ */ jsx37("th", { scope: "col", className: "py-1 pr-4 font-medium", children: "Component" }),
1818
+ /* @__PURE__ */ jsx37("th", { scope: "col", className: "py-1 pr-4 font-medium", children: "Lane" }),
1819
+ /* @__PURE__ */ jsx37("th", { scope: "col", className: "py-1 font-medium", children: "Covered by" })
1820
+ ] }) }),
1821
+ /* @__PURE__ */ jsx37("tbody", { children: graph.nodes.map((node) => /* @__PURE__ */ jsxs25("tr", { className: "border-t border-border align-top", children: [
1822
+ /* @__PURE__ */ jsxs25("th", { scope: "row", className: "py-1.5 pr-4 font-mono text-xs font-normal", children: [
1823
+ node.id,
1824
+ /* @__PURE__ */ jsx37(MarkBadge, { node })
1825
+ ] }),
1826
+ /* @__PURE__ */ jsx37("td", { className: "py-1.5 pr-4 text-xs text-muted-foreground", children: LANE_LABEL[node.kind] }),
1827
+ /* @__PURE__ */ jsx37("td", { className: "py-1.5", children: /* @__PURE__ */ jsx37("ul", { className: "flex flex-col gap-0.5", children: node.scenarioIds.map((id) => /* @__PURE__ */ jsx37("li", { children: /* @__PURE__ */ jsx37("a", { href: `#${id}`, className: "text-xs underline underline-offset-2", children: titleById.get(id) ?? id }) }, id)) }) })
1828
+ ] }, node.id)) })
1829
+ ] })
1830
+ ] });
1831
+ }
1832
+
1833
+ // src/components/Report.tsx
1834
+ import { jsx as jsx38, jsxs as jsxs26 } from "react/jsx-runtime";
1757
1835
  function Report(props) {
1758
1836
  const { report, customRenderers, renderers, className, title, dataTheme } = props;
1759
1837
  const result = unwrapReport(report);
1760
1838
  if (!result.ok) {
1761
- return /* @__PURE__ */ jsx37(ReportErrorShell, { error: result.error, className, title, dataTheme });
1839
+ return /* @__PURE__ */ jsx38(ReportErrorShell, { error: result.error, className, title, dataTheme });
1762
1840
  }
1763
1841
  const data = result.data;
1764
1842
  const hasContent = data.features.length > 0;
1765
- return /* @__PURE__ */ jsx37(ReportRoot, { report: data, customRenderers, renderers, children: /* @__PURE__ */ jsxs25("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: [
1766
- /* @__PURE__ */ jsx37("header", { className: "es-report-header", children: /* @__PURE__ */ jsx37(ReportTitleBlock, { title }) }),
1767
- hasContent ? /* @__PURE__ */ jsx37(ReportFeatureList, {}) : /* @__PURE__ */ jsx37(ReportEmpty, {})
1843
+ return /* @__PURE__ */ jsx38(ReportRoot, { report: data, customRenderers, renderers, children: /* @__PURE__ */ jsxs26("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: [
1844
+ /* @__PURE__ */ jsx38("header", { className: "es-report-header", children: /* @__PURE__ */ jsx38(ReportTitleBlock, { title }) }),
1845
+ /* @__PURE__ */ jsx38(ReportSpanGraph, {}),
1846
+ hasContent ? /* @__PURE__ */ jsx38(ReportFeatureList, {}) : /* @__PURE__ */ jsx38(ReportEmpty, {})
1768
1847
  ] }) });
1769
1848
  }
1770
1849
 
1771
1850
  // src/components/doc/MermaidView.tsx
1772
- import { useEffect as useEffect2, useRef, useState as useState3 } from "react";
1773
- import { Fragment as Fragment11, jsx as jsx38, jsxs as jsxs26 } from "react/jsx-runtime";
1851
+ import { useEffect as useEffect3, useRef as useRef2, useState as useState4 } from "react";
1852
+ import { Fragment as Fragment11, jsx as jsx39, jsxs as jsxs27 } from "react/jsx-runtime";
1774
1853
  function prefersDark(el) {
1775
1854
  const themed = el?.closest("[data-theme]");
1776
1855
  if (themed?.dataset.theme === "dark") return true;
@@ -1778,10 +1857,10 @@ function prefersDark(el) {
1778
1857
  return typeof window !== "undefined" && !!window.matchMedia?.("(prefers-color-scheme: dark)").matches;
1779
1858
  }
1780
1859
  function MermaidView({ entry, load }) {
1781
- const ref = useRef(null);
1782
- const [svg, setSvg] = useState3(null);
1783
- const [error, setError] = useState3(null);
1784
- useEffect2(() => {
1860
+ const ref = useRef2(null);
1861
+ const [svg, setSvg] = useState4(null);
1862
+ const [error, setError] = useState4(null);
1863
+ useEffect3(() => {
1785
1864
  let cancelled = false;
1786
1865
  const draw = async () => {
1787
1866
  let mermaid;
@@ -1824,9 +1903,9 @@ function MermaidView({ entry, load }) {
1824
1903
  };
1825
1904
  }, [entry.code, load]);
1826
1905
  if (svg) {
1827
- return /* @__PURE__ */ jsxs26("figure", { ref, className: "my-3", children: [
1828
- entry.title ? /* @__PURE__ */ jsx38("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
1829
- /* @__PURE__ */ jsx38(
1906
+ return /* @__PURE__ */ jsxs27("figure", { ref, className: "my-3", children: [
1907
+ entry.title ? /* @__PURE__ */ jsx39("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
1908
+ /* @__PURE__ */ jsx39(
1830
1909
  "div",
1831
1910
  {
1832
1911
  role: "img",
@@ -1837,8 +1916,8 @@ function MermaidView({ entry, load }) {
1837
1916
  )
1838
1917
  ] });
1839
1918
  }
1840
- return /* @__PURE__ */ jsxs26(Fragment11, { children: [
1841
- error ? /* @__PURE__ */ jsxs26(
1919
+ return /* @__PURE__ */ jsxs27(Fragment11, { children: [
1920
+ error ? /* @__PURE__ */ jsxs27(
1842
1921
  "p",
1843
1922
  {
1844
1923
  role: "alert",
@@ -1850,34 +1929,34 @@ function MermaidView({ entry, load }) {
1850
1929
  ]
1851
1930
  }
1852
1931
  ) : null,
1853
- /* @__PURE__ */ jsx38(MermaidSource, { entry, ref })
1932
+ /* @__PURE__ */ jsx39(MermaidSource, { entry, ref })
1854
1933
  ] });
1855
1934
  }
1856
1935
 
1857
1936
  // src/components/doc/MermaidDiagram.tsx
1858
- import { jsx as jsx39 } from "react/jsx-runtime";
1937
+ import { jsx as jsx40 } from "react/jsx-runtime";
1859
1938
  var mermaidLoader;
1860
1939
  function loadMermaid() {
1861
1940
  mermaidLoader ??= import("mermaid").then((m) => m.default);
1862
1941
  return mermaidLoader;
1863
1942
  }
1864
1943
  function MermaidDiagram({ entry, load = loadMermaid }) {
1865
- return /* @__PURE__ */ jsx39(MermaidView, { entry, load });
1944
+ return /* @__PURE__ */ jsx40(MermaidView, { entry, load });
1866
1945
  }
1867
1946
 
1868
1947
  // src/components/ScenarioExplorer.tsx
1869
- import { useState as useState4 } from "react";
1948
+ import { useState as useState5 } from "react";
1870
1949
 
1871
1950
  // src/components/ui/command.tsx
1872
1951
  import "react";
1873
1952
  import { Command as CommandPrimitive } from "cmdk";
1874
1953
  import { SearchIcon } from "lucide-react";
1875
- import { jsx as jsx40, jsxs as jsxs27 } from "react/jsx-runtime";
1954
+ import { jsx as jsx41, jsxs as jsxs28 } from "react/jsx-runtime";
1876
1955
  function Command({
1877
1956
  className,
1878
1957
  ...props
1879
1958
  }) {
1880
- return /* @__PURE__ */ jsx40(
1959
+ return /* @__PURE__ */ jsx41(
1881
1960
  CommandPrimitive,
1882
1961
  {
1883
1962
  "data-slot": "command",
@@ -1893,14 +1972,14 @@ function CommandInput({
1893
1972
  className,
1894
1973
  ...props
1895
1974
  }) {
1896
- return /* @__PURE__ */ jsxs27(
1975
+ return /* @__PURE__ */ jsxs28(
1897
1976
  "div",
1898
1977
  {
1899
1978
  "data-slot": "command-input-wrapper",
1900
1979
  className: "flex h-9 items-center gap-2 border-b px-3",
1901
1980
  children: [
1902
- /* @__PURE__ */ jsx40(SearchIcon, { className: "size-4 shrink-0 opacity-50" }),
1903
- /* @__PURE__ */ jsx40(
1981
+ /* @__PURE__ */ jsx41(SearchIcon, { className: "size-4 shrink-0 opacity-50" }),
1982
+ /* @__PURE__ */ jsx41(
1904
1983
  CommandPrimitive.Input,
1905
1984
  {
1906
1985
  "data-slot": "command-input",
@@ -1919,7 +1998,7 @@ function CommandList({
1919
1998
  className,
1920
1999
  ...props
1921
2000
  }) {
1922
- return /* @__PURE__ */ jsx40(
2001
+ return /* @__PURE__ */ jsx41(
1923
2002
  CommandPrimitive.List,
1924
2003
  {
1925
2004
  "data-slot": "command-list",
@@ -1934,7 +2013,7 @@ function CommandList({
1934
2013
  function CommandEmpty({
1935
2014
  ...props
1936
2015
  }) {
1937
- return /* @__PURE__ */ jsx40(
2016
+ return /* @__PURE__ */ jsx41(
1938
2017
  CommandPrimitive.Empty,
1939
2018
  {
1940
2019
  "data-slot": "command-empty",
@@ -1947,7 +2026,7 @@ function CommandGroup({
1947
2026
  className,
1948
2027
  ...props
1949
2028
  }) {
1950
- return /* @__PURE__ */ jsx40(
2029
+ return /* @__PURE__ */ jsx41(
1951
2030
  CommandPrimitive.Group,
1952
2031
  {
1953
2032
  "data-slot": "command-group",
@@ -1963,7 +2042,7 @@ function CommandItem({
1963
2042
  className,
1964
2043
  ...props
1965
2044
  }) {
1966
- return /* @__PURE__ */ jsx40(
2045
+ return /* @__PURE__ */ jsx41(
1967
2046
  CommandPrimitive.Item,
1968
2047
  {
1969
2048
  "data-slot": "command-item",
@@ -1977,7 +2056,7 @@ function CommandItem({
1977
2056
  }
1978
2057
 
1979
2058
  // src/components/ScenarioExplorer.tsx
1980
- import { jsx as jsx41, jsxs as jsxs28 } from "react/jsx-runtime";
2059
+ import { jsx as jsx42, jsxs as jsxs29 } from "react/jsx-runtime";
1981
2060
  var STATUS_FILTERS = [
1982
2061
  { value: "all", label: "All" },
1983
2062
  { value: "passed", label: "Passed" },
@@ -1986,24 +2065,24 @@ var STATUS_FILTERS = [
1986
2065
  { value: "pending", label: "Pending" }
1987
2066
  ];
1988
2067
  function ScenarioExplorer({ scenarios, summary }) {
1989
- const [status, setStatus] = useState4("all");
2068
+ const [status, setStatus] = useState5("all");
1990
2069
  const visible = status === "all" ? scenarios : scenarios.filter((s) => s.status === status);
1991
- return /* @__PURE__ */ jsxs28("div", { className: "font-sans text-foreground", children: [
1992
- summary ? /* @__PURE__ */ jsxs28("p", { className: "mb-3 text-sm text-muted-foreground", children: [
2070
+ return /* @__PURE__ */ jsxs29("div", { className: "font-sans text-foreground", children: [
2071
+ summary ? /* @__PURE__ */ jsxs29("p", { className: "mb-3 text-sm text-muted-foreground", children: [
1993
2072
  summary.total,
1994
2073
  " scenarios \xB7 ",
1995
- /* @__PURE__ */ jsxs28("span", { className: "text-pass", children: [
2074
+ /* @__PURE__ */ jsxs29("span", { className: "text-pass", children: [
1996
2075
  summary.passed,
1997
2076
  " passed"
1998
2077
  ] }),
1999
2078
  " \xB7",
2000
2079
  " ",
2001
- /* @__PURE__ */ jsxs28("span", { className: "text-fail", children: [
2080
+ /* @__PURE__ */ jsxs29("span", { className: "text-fail", children: [
2002
2081
  summary.failed,
2003
2082
  " failed"
2004
2083
  ] })
2005
2084
  ] }) : null,
2006
- /* @__PURE__ */ jsx41("div", { className: "mb-2 flex flex-wrap gap-1.5", role: "group", "aria-label": "Filter by status", children: STATUS_FILTERS.map((f) => /* @__PURE__ */ jsx41(
2085
+ /* @__PURE__ */ jsx42("div", { className: "mb-2 flex flex-wrap gap-1.5", role: "group", "aria-label": "Filter by status", children: STATUS_FILTERS.map((f) => /* @__PURE__ */ jsx42(
2007
2086
  "button",
2008
2087
  {
2009
2088
  type: "button",
@@ -2018,26 +2097,26 @@ function ScenarioExplorer({ scenarios, summary }) {
2018
2097
  },
2019
2098
  f.value
2020
2099
  )) }),
2021
- /* @__PURE__ */ jsxs28(
2100
+ /* @__PURE__ */ jsxs29(
2022
2101
  Command,
2023
2102
  {
2024
2103
  className: "rounded-lg border border-border bg-card",
2025
2104
  filter: (value, search) => value.toLowerCase().includes(search.toLowerCase()) ? 1 : 0,
2026
2105
  children: [
2027
- /* @__PURE__ */ jsx41(CommandInput, { placeholder: "Search scenarios\u2026" }),
2028
- /* @__PURE__ */ jsxs28(CommandList, { className: "max-h-none", children: [
2029
- /* @__PURE__ */ jsx41(CommandEmpty, { children: "No scenarios match your filters." }),
2030
- /* @__PURE__ */ jsx41(CommandGroup, { children: visible.map((s) => /* @__PURE__ */ jsx41(
2106
+ /* @__PURE__ */ jsx42(CommandInput, { placeholder: "Search scenarios\u2026" }),
2107
+ /* @__PURE__ */ jsxs29(CommandList, { className: "max-h-none", children: [
2108
+ /* @__PURE__ */ jsx42(CommandEmpty, { children: "No scenarios match your filters." }),
2109
+ /* @__PURE__ */ jsx42(CommandGroup, { children: visible.map((s) => /* @__PURE__ */ jsx42(
2031
2110
  CommandItem,
2032
2111
  {
2033
2112
  value: `${s.title} ${s.feature} ${s.tags.join(" ")}`,
2034
2113
  asChild: true,
2035
2114
  className: "cursor-pointer",
2036
- children: /* @__PURE__ */ jsxs28("a", { href: s.href, className: "flex items-center gap-3", children: [
2037
- /* @__PURE__ */ jsx41(Badge, { variant: s.status, children: s.status }),
2038
- /* @__PURE__ */ jsx41("span", { className: "min-w-0 flex-1 truncate font-medium", children: s.title }),
2039
- s.tags.slice(0, 3).map((t) => /* @__PURE__ */ jsx41(Badge, { variant: "tag", className: "hidden sm:inline-flex", children: t }, t)),
2040
- /* @__PURE__ */ jsx41("code", { className: "hidden font-mono text-xs text-muted-foreground md:inline", children: s.feature })
2115
+ children: /* @__PURE__ */ jsxs29("a", { href: s.href, className: "flex items-center gap-3", children: [
2116
+ /* @__PURE__ */ jsx42(Badge, { variant: s.status, children: s.status }),
2117
+ /* @__PURE__ */ jsx42("span", { className: "min-w-0 flex-1 truncate font-medium", children: s.title }),
2118
+ s.tags.slice(0, 3).map((t) => /* @__PURE__ */ jsx42(Badge, { variant: "tag", className: "hidden sm:inline-flex", children: t }, t)),
2119
+ /* @__PURE__ */ jsx42("code", { className: "hidden font-mono text-xs text-muted-foreground md:inline", children: s.feature })
2041
2120
  ] })
2042
2121
  },
2043
2122
  s.id