1,043 lines · 7 files · 25.4 kB
cases/122-premium-kpi-sparklines/chart.ts113 lines · dependency
cases/122-premium-kpi-sparklines/chart.ts
import { areaY, d3Curve, defineChart, lineY } from '@tanstack/charts'
import { motion } from '@tanstack/charts/motion'
import { scaleLinear } from '@tanstack/charts/scales/linear'
import { scalePoint } from '@tanstack/charts/scales/point'
import { curveMonotoneX } from 'd3-shape'
import type { PremiumKpiId, PremiumKpiMetric, PremiumKpiPoint } from './model'
const monotone = d3Curve(curveMonotoneX)
const accentFallbacks = {
revenue: '#6d5dfc',
customers: '#0f91c7',
churn: '#0c9b6c',
} satisfies Record<PremiumKpiId, string>
export const premiumKpiSpring = {
type: 'spring' as const,
stiffness: 180,
damping: 24,
mass: 0.8,
}
export function premiumKpiDefinition(metric: PremiumKpiMetric) {
const values = metric.rows.map((row) => row.value)
const minimum = Math.min(...values)
const maximum = Math.max(...values)
const padding = Math.max((maximum - minimum) * 0.16, 0.1)
const baseline = minimum - padding
const accent = `var(--premium-kpi-accent, ${accentFallbacks[metric.id]})`
const line = lineY(metric.rows, {
id: `${metric.id}-line`,
x: 'period',
y: 'value',
key: 'id',
stroke: accent,
strokeWidth: 2.4,
curve: monotone,
})
const marks =
metric.surface === 'area'
? [
areaY(metric.rows, {
id: `${metric.id}-area`,
x: 'period',
y: 'value',
y1: baseline,
key: 'id',
fill: `url(#${metric.id}-fill)`,
fillOpacity: 1,
curve: monotone,
}),
line,
]
: [line]
return defineChart(
defineChart({
guides: false,
marks,
gradients:
metric.surface === 'area'
? [
{
id: `${metric.id}-fill`,
x1: 0,
y1: 0,
x2: 0,
y2: 1,
stops: [
{
offset: 0,
color: accent,
opacity: 0.26,
},
{
offset: 1,
color: accent,
opacity: 0,
},
],
},
]
: [],
x: {
scale: scalePoint<number>()
.domain(metric.rows.map((row) => row.period))
.padding(0.08),
axis: false,
},
y: {
scale: scaleLinear().domain([baseline, maximum + padding]),
axis: false,
},
margin: { top: 4, right: 3, bottom: 3, left: 3 },
clip: true,
motion: { transition: premiumKpiSpring },
}),
{
focus: false,
pointer: false,
keyboard: false,
tooltip: false,
svgAnimation: false,
},
)
}
export function createPremiumKpiRenderer(initial = true) {
return motion<PremiumKpiPoint, number, number>({
initial,
respectReducedMotion: true,
transition: premiumKpiSpring,
})
}cases/122-premium-kpi-sparklines/model.ts99 lines · dependency
cases/122-premium-kpi-sparklines/model.ts
export type PremiumKpiId = 'revenue' | 'customers' | 'churn'
export interface PremiumKpiPoint {
readonly id: string
readonly period: number
readonly value: number
}
export interface PremiumKpiMetric {
readonly id: PremiumKpiId
readonly label: string
readonly value: string
readonly trend: string
readonly trendDirection: 'up' | 'down'
readonly surface: 'area' | 'line'
readonly rows: readonly PremiumKpiPoint[]
}
const stages = [
{
revenue: [
264_400, 281_200, 276_800, 302_600, 319_100, 337_500, 331_800, 356_200,
371_600, 365_900, 394_300, 412_840,
],
customers: [
2_810, 2_950, 3_030, 3_170, 3_120, 3_290, 3_410, 3_500, 3_590, 3_670,
3_750, 3_842,
],
churn: [2.8, 2.6, 2.7, 2.5, 2.4, 2.3, 2.35, 2.15, 2.1, 1.95, 1.86, 1.7],
},
{
revenue: [
279_200, 288_900, 286_400, 315_300, 329_800, 351_500, 345_900, 369_600,
388_100, 381_700, 406_900, 429_180,
],
customers: [
2_940, 3_020, 3_140, 3_240, 3_210, 3_380, 3_500, 3_610, 3_690, 3_790,
3_880, 3_976,
],
churn: [
2.7, 2.55, 2.62, 2.42, 2.33, 2.2, 2.24, 2.05, 1.98, 1.84, 1.75, 1.62,
],
},
] as const
export function premiumKpisForRevision(
revision: number,
): readonly PremiumKpiMetric[] {
const stage = stages[Math.abs(revision) % stages.length] ?? stages[0]
return [
{
id: 'revenue',
label: 'Monthly revenue',
value: formatRevenue(last(stage.revenue)),
trend: revision % 2 === 0 ? '+9.2%' : '+10.6%',
trendDirection: 'up',
surface: 'area',
rows: points('revenue', stage.revenue),
},
{
id: 'customers',
label: 'Active customers',
value: last(stage.customers).toLocaleString('en-US'),
trend: revision % 2 === 0 ? '+6.4%' : '+7.1%',
trendDirection: 'up',
surface: 'line',
rows: points('customers', stage.customers),
},
{
id: 'churn',
label: 'Net churn',
value: `${last(stage.churn).toFixed(1)}%`,
trend: revision % 2 === 0 ? '−0.5 pt' : '−0.6 pt',
trendDirection: 'down',
surface: 'area',
rows: points('churn', stage.churn),
},
]
}
function points(
metric: PremiumKpiId,
values: readonly number[],
): readonly PremiumKpiPoint[] {
return values.map((value, period) => ({
id: `${metric}-${period}`,
period,
value,
}))
}
function formatRevenue(value: number) {
return `$${(value / 1_000).toFixed(1)}K`
}
function last(values: readonly number[]) {
return values[values.length - 1] ?? 0
}cases/122-premium-kpi-sparklines/tanstack.ts6 lines · entry
cases/122-premium-kpi-sparklines/tanstack.ts
export {
createPremiumKpiRenderer,
premiumKpiDefinition,
premiumKpiSpring,
} from './chart'
export { catalogComponent, mount } from './view'cases/122-premium-kpi-sparklines/view.tsx364 lines · dependency
cases/122-premium-kpi-sparklines/view.tsx
import { forwardRef, useImperativeHandle, useMemo, useRef } from 'react'
import { Chart } from '@tanstack/charts/react/core'
import { settleChartMotion } from '../../shared/motion'
import { createPremiumKpiRenderer, premiumKpiDefinition } from './chart'
import { premiumKpisForRevision } from './model'
import { reactMount } from '../../shared/react-mount'
import type { CSSProperties, RefObject } from 'react'
import type { ConformanceTestDriver } from '../../types'
import type { ReactConformanceProps } from '../../shared/react-mount'
import type { PremiumKpiMetric } from './model'
const fullGap = 12
export const PremiumKpiSparklines = forwardRef<
ConformanceTestDriver,
ReactConformanceProps
>(function PremiumKpiSparklines({ input, idPrefix }, ref) {
const rootRef = useRef<HTMLElement>(null)
const metrics = premiumKpisForRevision(input.revision)
useImperativeHandle(
ref,
() => ({
resolveTarget() {
return null
},
readState() {
return {
revision: input.revision,
chartCount:
rootRef.current?.querySelectorAll('.ts-chart-host').length ?? 0,
}
},
settle() {
const root = rootRef.current
if (!root) return
return Promise.all(
[...root.querySelectorAll<HTMLElement>('.ts-chart-host')].map(
(host) => settleChartMotion(host, 2_500),
),
).then(() => undefined)
},
}),
[input.revision],
)
if (input.preview) {
return (
<PreviewGrid
metrics={metrics}
width={input.width}
height={input.height}
idPrefix={idPrefix}
rootRef={rootRef}
/>
)
}
const layout = fullLayout(input.width, input.height)
return (
<section
ref={rootRef}
data-conformance-view="main"
data-premium-kpi-shell=""
aria-label="Business metrics"
className="premium-kpi-shell"
style={{ width: input.width, height: input.height }}
>
<style>{premiumKpiStyles}</style>
<div
className="premium-kpi-grid"
style={{
gridTemplateColumns: layout.columns,
gridTemplateRows: layout.rows,
gap: fullGap,
}}
>
{metrics.map((metric) => (
<KpiCard
key={metric.id}
metric={metric}
width={layout.cardWidth}
height={layout.cardHeight}
idPrefix={idPrefix}
/>
))}
</div>
</section>
)
})
export const catalogComponent = PremiumKpiSparklines
export const mount = reactMount(PremiumKpiSparklines)
function KpiCard({
metric,
width,
height,
idPrefix,
preview = false,
}: {
readonly metric: PremiumKpiMetric
readonly width: number
readonly height: number
readonly idPrefix?: string
readonly preview?: boolean
}) {
const definition = useMemo(() => premiumKpiDefinition(metric), [metric])
const renderer = useMemo(() => createPremiumKpiRenderer(!preview), [preview])
const compact = preview || width < 200 || height < 150
const padding = compact ? 9 : 18
const headerHeight = compact ? 44 : 72
const chartWidth = Math.max(24, width - padding * 2)
const chartHeight = Math.max(24, height - padding * 2 - headerHeight)
return (
<article
data-premium-kpi={metric.id}
data-conformance-view={metric.id}
data-compact={compact || undefined}
className="premium-kpi-card"
style={
{
'--premium-kpi-card-width': `${width}px`,
'--premium-kpi-card-height': `${height}px`,
} as CSSProperties
}
>
<header className="premium-kpi-header">
<span className="premium-kpi-label">{metric.label}</span>
<span
className="premium-kpi-trend"
aria-label={`${metric.trendDirection === 'up' ? 'Up' : 'Down'} ${metric.trend}`}
>
<span aria-hidden="true">
{metric.trendDirection === 'up' ? '↑' : '↓'}
</span>{' '}
{metric.trend}
</span>
<strong className="premium-kpi-value">{metric.value}</strong>
</header>
<div className="premium-kpi-chart">
<Chart
idPrefix={idPrefix ? `${idPrefix}-${metric.id}` : undefined}
definition={definition}
renderer={renderer}
width={chartWidth}
height={chartHeight}
ariaLabel={`${metric.label} trend, ending at ${metric.value}`}
/>
</div>
</article>
)
}
function PreviewGrid({
metrics,
width,
height,
idPrefix,
rootRef,
}: {
readonly metrics: readonly PremiumKpiMetric[]
readonly width: number
readonly height: number
readonly idPrefix?: string
readonly rootRef: RefObject<HTMLElement | null>
}) {
const gap = 5
const primaryWidth = Math.max(1, Math.floor((width - gap) * 0.59))
const secondaryWidth = Math.max(1, width - gap - primaryWidth)
const secondaryHeight = Math.max(1, Math.floor((height - gap) / 2))
const [primary, ...secondary] = metrics
return (
<section
ref={rootRef}
data-catalog-preview-composition="premium-kpi-sparklines"
className="premium-kpi-shell premium-kpi-preview"
style={{ width, height }}
>
<style>{premiumKpiStyles}</style>
{primary ? (
<KpiCard
metric={primary}
width={primaryWidth}
height={height}
idPrefix={idPrefix}
preview
/>
) : null}
<div
className="premium-kpi-preview-stack"
style={{ width: secondaryWidth, height }}
>
{secondary.map((metric) => (
<KpiCard
key={metric.id}
metric={metric}
width={secondaryWidth}
height={secondaryHeight}
idPrefix={idPrefix}
preview
/>
))}
</div>
</section>
)
}
function fullLayout(width: number, height: number) {
const padding = 14
const availableWidth = Math.max(1, width - padding * 2)
const availableHeight = Math.max(1, height - padding * 2)
const horizontal = width >= 680 || width / height >= 1.2
return horizontal
? {
columns: 'repeat(3, minmax(0, 1fr))',
rows: 'minmax(0, 1fr)',
cardWidth: Math.max(1, (availableWidth - fullGap * 2) / 3),
cardHeight: availableHeight,
}
: {
columns: 'minmax(0, 1fr)',
rows: 'repeat(3, minmax(0, 1fr))',
cardWidth: availableWidth,
cardHeight: Math.max(1, (availableHeight - fullGap * 2) / 3),
}
}
const premiumKpiStyles = `
.premium-kpi-shell {
--premium-kpi-canvas: light-dark(#f5f6f8, #09090b);
--premium-kpi-card: light-dark(rgba(255, 255, 255, 0.92), rgba(24, 24, 27, 0.9));
--premium-kpi-foreground: light-dark(#18181b, #fafafa);
--premium-kpi-muted: light-dark(#71717a, #a1a1aa);
--premium-kpi-border: light-dark(rgba(24, 24, 27, 0.09), rgba(250, 250, 250, 0.1));
--premium-kpi-shadow: light-dark(rgba(24, 24, 27, 0.07), rgba(0, 0, 0, 0.34));
color-scheme: inherit;
box-sizing: border-box;
display: grid;
place-items: stretch;
padding: 14px;
overflow: hidden;
color: var(--premium-kpi-foreground);
background: var(--premium-kpi-canvas);
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
.premium-kpi-grid {
display: grid;
min-width: 0;
min-height: 0;
}
.premium-kpi-card {
--premium-kpi-accent: #6d5dfc;
box-sizing: border-box;
display: flex;
flex-direction: column;
width: var(--premium-kpi-card-width);
height: var(--premium-kpi-card-height);
min-width: 0;
min-height: 0;
overflow: hidden;
padding: 18px;
border: 1px solid var(--premium-kpi-border);
border-radius: 18px;
background: var(--premium-kpi-card);
box-shadow: 0 14px 42px var(--premium-kpi-shadow);
}
.premium-kpi-card[data-premium-kpi="customers"] {
--premium-kpi-accent: #0f91c7;
}
.premium-kpi-card[data-premium-kpi="churn"] {
--premium-kpi-accent: #0c9b6c;
}
.premium-kpi-header {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: baseline;
column-gap: 8px;
flex: 0 0 72px;
}
.premium-kpi-label {
overflow: hidden;
color: var(--premium-kpi-muted);
font-size: 12px;
font-weight: 620;
line-height: 1.2;
text-overflow: ellipsis;
white-space: nowrap;
}
.premium-kpi-value {
grid-column: 1 / -1;
margin-top: 7px;
font-size: clamp(24px, 3.1vw, 36px);
font-weight: 730;
letter-spacing: -0.055em;
line-height: 1;
}
.premium-kpi-trend {
color: var(--premium-kpi-accent);
font-size: 11px;
font-weight: 700;
line-height: 1;
white-space: nowrap;
}
.premium-kpi-chart {
display: grid;
flex: 1 1 auto;
min-width: 0;
min-height: 0;
align-items: end;
}
.premium-kpi-preview {
display: flex;
gap: 5px;
padding: 0;
background: transparent;
}
.premium-kpi-card[data-compact] {
padding: 9px;
border-radius: 11px;
box-shadow: 0 6px 18px var(--premium-kpi-shadow);
}
.premium-kpi-card[data-compact] .premium-kpi-header {
flex-basis: 44px;
column-gap: 4px;
}
.premium-kpi-card[data-compact] .premium-kpi-label {
font-size: 7px;
}
.premium-kpi-card[data-compact] .premium-kpi-value {
margin-top: 3px;
font-size: 15px;
}
.premium-kpi-card[data-compact] .premium-kpi-trend {
font-size: 7px;
}
.premium-kpi-preview-stack {
display: flex;
flex-direction: column;
gap: 5px;
min-width: 0;
}
`shared/motion.ts28 lines · dependency
shared/motion.ts
export function readChartMotionState(root: ParentNode) {
return (
root.querySelector('svg.ts-chart')?.getAttribute('data-ts-motion-state') ??
null
)
}
export function settleChartMotion(root: HTMLElement, timeout: number) {
const view = root.ownerDocument.defaultView
if (!view) return Promise.resolve()
const started = view.performance.now()
return new Promise<void>((resolve) => {
const check = () => {
const state = readChartMotionState(root)
if (
state === 'finished' ||
state === null ||
view.performance.now() - started >= timeout
) {
resolve()
return
}
view.requestAnimationFrame(check)
}
check()
})
}shared/react-mount.ts57 lines · dependency
shared/react-mount.ts
import { createElement } from 'react'
import { flushSync } from 'react-dom'
import { createRoot } from 'react-dom/client'
import type { ForwardRefExoticComponent, RefAttributes } from 'react'
import type {
ConformanceInput,
ConformanceMount,
ConformanceTestDriver,
} from '../types'
export interface ReactConformanceProps {
input: ConformanceInput
idPrefix?: string
}
export type ReactConformanceComponent = ForwardRefExoticComponent<
ReactConformanceProps & RefAttributes<ConformanceTestDriver>
>
export function reactMount(
Component: ReactConformanceComponent,
): ConformanceMount {
return (container, input) => {
const root = createRoot(container)
let activeDriver: ConformanceTestDriver | null = null
const driver = new Proxy({} as ConformanceTestDriver, {
get(_target, property) {
const value = activeDriver?.[property as keyof ConformanceTestDriver]
return typeof value === 'function' ? value.bind(activeDriver) : value
},
})
const render = (nextInput: ConformanceInput) => {
flushSync(() => {
root.render(
createElement(Component, {
input: nextInput,
ref: (nextDriver: ConformanceTestDriver | null) => {
activeDriver = nextDriver
},
}),
)
})
}
render(input)
return {
update: render,
driver,
destroy() {
flushSync(() => {
root.unmount()
})
},
}
}
}types.ts376 lines · dependency
types.ts
export type ConformanceReferenceRenderer =
'observable-plot' | 'recharts' | 'echarts'
export type ConformanceRenderer = ConformanceReferenceRenderer | 'tanstack'
export type ConformanceSupport = 'native' | 'composed' | 'gap' | 'deferred'
export type ConformanceGeometryRole =
| 'arc'
| 'area'
| 'arrow'
| 'bar'
| 'cell'
| 'contour'
| 'delaunay'
| 'density'
| 'dot'
| 'frame'
| 'geo'
| 'hexagon'
| 'line'
| 'link'
| 'rect'
| 'radar'
| 'regression'
| 'rule'
| 'text'
| 'tick'
| 'vector'
| 'voronoi'
| 'waffle'
export interface ConformanceInput {
width: number
height: number
revision: number
interactive?: boolean
/** Use lower-detail geometry suited to compact catalog cards. */
preview?: boolean
/** True only for semantic browser scenarios, not catalog or visual mounts. */
behavior?: boolean
}
export interface ConformanceHandle {
update: (input: ConformanceInput) => void
driver?: ConformanceTestDriver
destroy: () => void
}
export type ConformanceMount = (
container: HTMLElement,
input: ConformanceInput,
) => ConformanceHandle
export interface ConformanceGeometryExpectation {
id?: string
view?: string
role: ConformanceGeometryRole
count: number
maxCount?: number
rendererRoles?: Partial<Record<ConformanceRenderer, ConformanceGeometryRole>>
}
export type ConformanceAxis = 'x' | 'y' | 'fx' | 'fy'
export interface ConformanceGuideExpectation {
id: string
axis:
| ConformanceAxis
| (Record<'tanstack', ConformanceAxis> &
Partial<Record<ConformanceReferenceRenderer, ConformanceAxis>>)
sequence?: readonly string[]
maxRepeat?: number
}
export type ConformanceJsonValue =
| null
| boolean
| number
| string
| readonly ConformanceJsonValue[]
| ConformanceJsonObject
export interface ConformanceJsonObject {
readonly [key: string]: ConformanceJsonValue
}
export interface ConformanceTarget {
view?: string
anchor: string
}
export type ConformanceRenderedTarget =
| {
selector: string
index?: number
role?: never
name?: never
exact?: never
root?: never
page?: never
}
| {
role: string
name?: string
exact?: boolean
index?: number
selector?: never
root?: never
page?: never
}
| {
root: true
selector?: never
role?: never
name?: never
exact?: never
index?: never
page?: never
}
| {
page: true
selector?: never
role?: never
name?: never
exact?: never
index?: never
root?: never
}
export interface ConformanceResolvedTarget {
/** Viewport-relative client coordinate used by Playwright mouse input. */
x: number
/** Viewport-relative client coordinate used by Playwright mouse input. */
y: number
/** Optional element to focus before a real Playwright keyboard action. */
focusElement?: HTMLElement | SVGElement
}
export interface ConformanceGeometryQuery {
view?: string
role: ConformanceGeometryRole
}
export interface ConformanceGeometrySample {
/** Viewport-relative client box, matching getBoundingClientRect coordinates. */
x: number
y: number
width: number
height: number
paint?: string
}
export interface ConformanceTestDriver {
/**
* Benchmark-only semantic bridge. Case metadata names anchors; each renderer
* resolves those anchors without exposing renderer-specific selectors.
*/
resolveTarget: (target: ConformanceTarget) => ConformanceResolvedTarget | null
readState: () => ConformanceJsonObject
geometry?: (
query: ConformanceGeometryQuery,
) => readonly ConformanceGeometrySample[]
/**
* Viewport-relative logical view bounds. Multi-grid renderers may expose
* independent views without separate DOM roots.
*/
viewBounds?: (view?: string) => ConformanceGeometrySample | null
settle?: () => void | Promise<void>
}
export type ConformanceStateAssertion =
| {
path: string
equals: ConformanceJsonValue
}
| {
path: string
includes: ConformanceJsonValue
}
| {
path: string
approx: number
tolerance: number
}
type ConformanceRenderedStringMatcher =
| {
equals: string | null
includes?: never
}
| {
includes: string
equals?: never
}
type ConformanceRenderedNumberMatcher =
| {
equals: number
approx?: never
tolerance?: never
atLeast?: never
atMost?: never
}
| {
approx: number
tolerance: number
equals?: never
atLeast?: never
atMost?: never
}
| {
atLeast: number
equals?: never
approx?: never
tolerance?: never
atMost?: never
}
| {
atMost: number
equals?: never
approx?: never
tolerance?: never
atLeast?: never
}
export type ConformanceRenderedAssertion =
| ({
target: ConformanceRenderedTarget
property: 'count'
} & ConformanceRenderedNumberMatcher)
| ({
target: ConformanceRenderedTarget
property: 'text'
} & ConformanceRenderedStringMatcher)
| ({
target: ConformanceRenderedTarget
property: 'attribute'
attribute: string
} & ConformanceRenderedStringMatcher)
| {
target: ConformanceRenderedTarget
property: 'visible' | 'focused'
equals: boolean
}
| ({
target: ConformanceRenderedTarget
property:
| 'scrollLeft'
| 'scrollTop'
| 'scrollWidth'
| 'scrollHeight'
| 'clientWidth'
| 'clientHeight'
| 'width'
| 'height'
} & ConformanceRenderedNumberMatcher)
| {
target: ConformanceRenderedTarget
property: 'contained'
within?: ConformanceRenderedTarget
tolerance?: number
equals: true
}
export type ConformanceInteractionStep =
| {
type: 'pointerMove'
target: ConformanceTarget
steps?: number
}
| {
type: 'pointerDown'
target: ConformanceTarget
}
| {
type: 'pointerUp'
target: ConformanceTarget
}
| {
type: 'pointerCancel'
}
| {
type: 'pointerLeave'
view?: string
}
| {
type: 'update'
revision: number
}
| {
type: 'click'
target: ConformanceTarget
}
| {
type: 'key'
key: string
target?: ConformanceTarget
}
| {
type: 'drag'
from: ConformanceTarget
to: ConformanceTarget
steps?: number
}
| {
type: 'wheel'
target: ConformanceTarget
deltaX?: number
deltaY?: number
steps?: number
deltaMode?: 'pixel' | 'line' | 'page'
}
| {
type: 'touchTap'
target: ConformanceTarget
}
| {
type: 'touchDrag'
from: ConformanceTarget
to: ConformanceTarget
steps?: number
cancel?: boolean
}
| {
type: 'wait'
durationMs: number
}
| {
type: 'assert'
assertions: readonly ConformanceStateAssertion[]
}
| {
type: 'assertRendered'
assertions: readonly ConformanceRenderedAssertion[]
}
| {
type: 'screenshot'
name: string
view?: string
}
export interface ConformanceInteractionScenario {
id: string
steps: readonly ConformanceInteractionStep[]
}
export interface ConformanceCaseMeta {
schemaVersion: 1
referenceRenderer?: ConformanceReferenceRenderer
order: number
id: string
title: string
family: string
intent: string
support: ConformanceSupport
features: readonly string[]
geometry: readonly ConformanceGeometryExpectation[]
minimumGeometrySimilarity?: number
guideAssertions?: readonly ConformanceGuideExpectation[]
interactionScenarios?: readonly ConformanceInteractionScenario[]
source: {
title: string
url: string
}
ai: {
create: string
maintain: string
}
}
export interface ConformanceImplementationModule {
mount: ConformanceMount
/** Definition-only mount used by compact generated catalog previews. */
catalogCase?: { mount: ConformanceMount }
}