Lab
Bridge
// What every surface can draw: one bar, and a figure around the bars.
interface Surface {
bar(label: string, value: number, largest: number): string;
figure(title: string, bars: string[], note: string): string;
}
const escapeXml = (text: string) =>
text.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>');
// Plain text for the weekly digest: a bar is up to 20 blocks.
class TextSurface implements Surface {
bar(label: string, value: number, largest: number): string {
const blocks = '█'.repeat(Math.floor((value * 20) / largest));
return `${label} ${blocks} ${value}`;
}
figure(title: string, bars: string[], note: string): string {
return [title, ...bars, note].join('\n');
}
}
// SVG for the dashboard: a bar is up to 200 units wide.
class SvgSurface implements Surface {
bar(label: string, value: number, largest: number): string {
const width = Math.floor((value * 200) / largest);
const tip = `${escapeXml(label)}: ${value}`;
return `<rect width="${width}"><title>${tip}</title></rect>`;
}
figure(title: string, bars: string[], note: string): string {
const head = `<title>${escapeXml(title)}</title>`;
return `<svg>${head}${bars.join('')}<text>${escapeXml(note)}</text></svg>`;
}
}
type Point = { label: string; value: number };
// Top categories decides what to show: the three biggest, largest first.
class TopCategories {
private readonly surface: Surface;
constructor(surface: Surface) {
this.surface = surface;
}
draw(title: string, points: Point[]): string {
const top = [...points].sort((a, b) => b.value - a.value).slice(0, 3);
const largest = top[0].value;
const bars = top.map((p) => this.surface.bar(p.label, p.value, largest));
const note = `${top.length} of ${points.length} shown`;
return this.surface.figure(`Top categories: ${title}`, bars, note);
}
}
// Weekly trend decides what to show: every week in order, and the change.
class WeeklyTrend {
private readonly surface: Surface;
constructor(surface: Surface) {
this.surface = surface;
}
draw(title: string, points: Point[]): string {
const largest = Math.max(...points.map((p) => p.value));
const bars = points.map((p) => this.surface.bar(p.label, p.value, largest));
const change = points[points.length - 1].value - points[0].value;
const note = `Change: ${change >= 0 ? '+' : ''}${change}`;
return this.surface.figure(`Weekly trend: ${title}`, bars, note);
}
}
// The caller connects one chart to one surface.
const categories: Point[] = [
{ label: 'Billing & refunds', value: 12 },
{ label: 'Login', value: 30 },
{ label: 'Exports', value: 7 },
{ label: 'Mobile', value: 18 },
];
const weeks: Point[] = [
{ label: 'W1', value: 20 },
{ label: 'W2', value: 26 },
{ label: 'W3', value: 21 },
];
const svg = new SvgSurface();
const text = new TextSurface();
const title = 'Support tickets';
const dashboard = new TopCategories(svg).draw(title, categories);
const digest = new TopCategories(text).draw(title, categories);
const trend = new WeeklyTrend(text).draw(title, weeks);
console.log(dashboard);
console.log(digest);
console.log(trend);
Inspect recorded facts and calls
Which data appears?
Top categories
TopCategoriesWeekly trend
WeeklyTrendplain facts
SurfaceHow is it drawn?
SVG dashboard
SvgSurfaceText digest
TextSurfaceRecorded dataawaiting values
No complete input collection observed yet.
Received across the boundary
bar(label, value, largest) Awaiting a display call Observed representationNo result yet
The display’s returned value appears when it is recorded.
Play or step to follow the recorded selection and display calls.
Role cards explain the design. Highlighted roles come from the recording; “called” cells show observed delegation, not all possible combinations.