@git-snitch/renderer 0.0.10 → 0.0.12
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/charts.d.ts +52 -1
- package/dist/charts.d.ts.map +1 -1
- package/dist/charts.js +69 -0
- package/dist/layout.js +3 -3
- package/dist/template/report-template.html +18 -18
- package/package.json +3 -3
- package/src/ai-usage.tsx +108 -0
- package/src/charts-route.tsx +13 -0
- package/src/charts.tsx +192 -1
- package/src/layout.tsx +3 -3
- package/src/overview.tsx +3 -0
- package/src/scan-routes.tsx +77 -12
package/dist/charts.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CommitRecord, ContributorSummary, RepoReportData, ScanProjectReport, ScanReportData } from "@git-snitch/core";
|
|
1
|
+
import type { AiUsageBreakdownItem, CommitRecord, ContributorSummary, RepoReportData, ReportAiUsageProjectSummary, ScanProjectReport, ScanReportData } from "@git-snitch/core";
|
|
2
2
|
export type CommitActivityPoint = {
|
|
3
3
|
readonly period: string;
|
|
4
4
|
readonly commits: number;
|
|
@@ -55,6 +55,32 @@ export type ActivityHeatmapCell = {
|
|
|
55
55
|
readonly hour: string;
|
|
56
56
|
readonly commits: number;
|
|
57
57
|
};
|
|
58
|
+
export type AiUsageBreakdownPoint = {
|
|
59
|
+
readonly name: string;
|
|
60
|
+
readonly messages: number;
|
|
61
|
+
readonly tokens: number;
|
|
62
|
+
readonly cost: number;
|
|
63
|
+
};
|
|
64
|
+
export type ScanCommitSlice = {
|
|
65
|
+
readonly name: string;
|
|
66
|
+
readonly commits: number;
|
|
67
|
+
};
|
|
68
|
+
export type ScanChurnSlice = {
|
|
69
|
+
readonly name: string;
|
|
70
|
+
readonly additions: number;
|
|
71
|
+
readonly deletions: number;
|
|
72
|
+
readonly churn: number;
|
|
73
|
+
};
|
|
74
|
+
export type ScanAiProjectSlice = {
|
|
75
|
+
readonly name: string;
|
|
76
|
+
readonly messages: number;
|
|
77
|
+
readonly tokens: number;
|
|
78
|
+
};
|
|
79
|
+
export type ScanAiModelSlice = {
|
|
80
|
+
readonly name: string;
|
|
81
|
+
readonly messages: number;
|
|
82
|
+
readonly tokens: number;
|
|
83
|
+
};
|
|
58
84
|
export declare function deriveCommitActivityData(report: Pick<RepoReportData, "analysis">): readonly CommitActivityPoint[];
|
|
59
85
|
export declare function deriveContributorPieData(contributors: readonly ContributorSummary[]): readonly ContributorPieSlice[];
|
|
60
86
|
export declare function deriveLanguageDistributionData(report: Pick<RepoReportData | ScanReportData, "analysis">): readonly LanguageDistributionSlice[];
|
|
@@ -66,6 +92,7 @@ export declare function deriveContributionCalendarData(commits: readonly CommitR
|
|
|
66
92
|
export declare function deriveVelocityData(report: Pick<RepoReportData, "analysis">): readonly VelocityPoint[];
|
|
67
93
|
export declare function deriveCodeOwnershipData(contributors: readonly ContributorSummary[]): readonly CodeOwnershipPoint[];
|
|
68
94
|
export declare function deriveProjectsComparisonData(projects: readonly ScanProjectReport[]): readonly ProjectComparisonPoint[];
|
|
95
|
+
export declare function deriveAiUsageBreakdownData(rows: readonly AiUsageBreakdownItem[]): readonly AiUsageBreakdownPoint[];
|
|
69
96
|
export declare function deriveActivityHeatmapData(commits: readonly CommitRecord[]): readonly ActivityHeatmapCell[];
|
|
70
97
|
export declare function CommitActivityChart({ data }: {
|
|
71
98
|
readonly data: readonly CommitActivityPoint[];
|
|
@@ -100,7 +127,31 @@ export declare function CodeOwnershipChart({ data }: {
|
|
|
100
127
|
export declare function ProjectsComparisonChart({ data }: {
|
|
101
128
|
readonly data: readonly ProjectComparisonPoint[];
|
|
102
129
|
}): import("react/jsx-runtime").JSX.Element;
|
|
130
|
+
export declare function AiUsageBreakdownChart({ title, description, data }: {
|
|
131
|
+
readonly title: string;
|
|
132
|
+
readonly description: string;
|
|
133
|
+
readonly data: readonly AiUsageBreakdownPoint[];
|
|
134
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
103
135
|
export declare function ActivityHeatmap({ data }: {
|
|
104
136
|
readonly data: readonly ActivityHeatmapCell[];
|
|
105
137
|
}): import("react/jsx-runtime").JSX.Element;
|
|
138
|
+
export declare function ScanCommitBarChart({ data }: {
|
|
139
|
+
readonly data: readonly ScanCommitSlice[];
|
|
140
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
141
|
+
export declare function ScanChurnPieChart({ data }: {
|
|
142
|
+
readonly data: readonly ScanChurnSlice[];
|
|
143
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
144
|
+
export declare function ScanAiMessagesPieChart({ data }: {
|
|
145
|
+
readonly data: readonly ScanAiProjectSlice[];
|
|
146
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
147
|
+
export declare function ScanAiTokensBarChart({ data }: {
|
|
148
|
+
readonly data: readonly ScanAiProjectSlice[];
|
|
149
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
150
|
+
export declare function ScanAiModelsPieChart({ data }: {
|
|
151
|
+
readonly data: readonly ScanAiModelSlice[];
|
|
152
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
153
|
+
export declare function deriveScanCommitData(projects: readonly ScanProjectReport[]): readonly ScanCommitSlice[];
|
|
154
|
+
export declare function deriveScanChurnData(projects: readonly ScanProjectReport[]): readonly ScanChurnSlice[];
|
|
155
|
+
export declare function deriveScanAiPerProjectData(projects: readonly ScanProjectReport[]): readonly ScanAiProjectSlice[];
|
|
156
|
+
export declare function deriveScanAiModelsData(aiUsage: ReportAiUsageProjectSummary): readonly ScanAiModelSlice[];
|
|
106
157
|
//# sourceMappingURL=charts.d.ts.map
|
package/dist/charts.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"charts.d.ts","sourceRoot":"","sources":["../src/charts.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"charts.d.ts","sourceRoot":"","sources":["../src/charts.tsx"],"names":[],"mappings":"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"}
|
package/dist/charts.js
CHANGED
|
@@ -144,6 +144,13 @@ export function deriveProjectsComparisonData(projects) {
|
|
|
144
144
|
}))
|
|
145
145
|
.sort((left, right) => right.commits - left.commits || left.project.localeCompare(right.project));
|
|
146
146
|
}
|
|
147
|
+
export function deriveAiUsageBreakdownData(rows) {
|
|
148
|
+
return rows
|
|
149
|
+
.filter((row) => row.records > 0 || row.tokens.total > 0 || row.cost > 0)
|
|
150
|
+
.map((row) => ({ name: row.key, messages: row.records, tokens: row.tokens.total, cost: row.cost }))
|
|
151
|
+
.sort((left, right) => right.tokens - left.tokens || right.messages - left.messages || left.name.localeCompare(right.name))
|
|
152
|
+
.slice(0, 8);
|
|
153
|
+
}
|
|
147
154
|
export function deriveActivityHeatmapData(commits) {
|
|
148
155
|
const counts = new Map();
|
|
149
156
|
for (const commit of commits) {
|
|
@@ -195,9 +202,71 @@ export function CodeOwnershipChart({ data }) {
|
|
|
195
202
|
export function ProjectsComparisonChart({ data }) {
|
|
196
203
|
return (_jsx(ChartPanel, { title: "Projects comparison", description: "Repository activity across a scan.", isEmpty: !hasPositiveValue(data, (item) => [item.commits, item.contributors, item.filesChanged]), emptyTitle: "No projects to compare", emptyDescription: "Project comparison needs at least one scanned repository with activity.", children: _jsx(ChartContainer, { config: { commits: { label: "Commits", color: chartPalette[1] }, contributors: { label: "Contributors", color: chartPalette[3] } }, className: "h-72 w-full", children: _jsxs(BarChart, { data: data, layout: "vertical", margin: { left: 8, right: 16, top: 8, bottom: 0 }, children: [_jsx(CartesianGrid, { horizontal: false }), _jsx(XAxis, { type: "number", hide: true }), _jsx(YAxis, { dataKey: "project", type: "category", width: 112, tickLine: false, axisLine: false }), _jsx(ChartTooltip, { content: _jsx(ChartTooltipContent, {}) }), _jsx(Bar, { dataKey: "commits", fill: "var(--color-commits)", radius: [0, 3, 3, 0], ...staticChartProps }), _jsx(Bar, { dataKey: "contributors", fill: "var(--color-contributors)", radius: [0, 3, 3, 0], ...staticChartProps })] }) }) }));
|
|
197
204
|
}
|
|
205
|
+
export function AiUsageBreakdownChart({ title, description, data }) {
|
|
206
|
+
return (_jsx(ChartPanel, { title: title, description: description, isEmpty: !hasPositiveValue(data, (item) => [item.messages, item.tokens]), emptyTitle: `No ${title.toLowerCase()} to chart`, emptyDescription: "AI usage charts need matched local assistant records with model or harness metadata.", children: _jsx(ChartContainer, { config: { tokens: { label: "Tokens", color: chartPalette[2] }, messages: { label: "Messages", color: chartPalette[4] } }, className: "h-72 w-full", children: _jsxs(BarChart, { data: data, layout: "vertical", margin: { left: 8, right: 16, top: 8, bottom: 0 }, children: [_jsx(CartesianGrid, { horizontal: false }), _jsx(XAxis, { type: "number", hide: true }), _jsx(YAxis, { dataKey: "name", type: "category", width: 124, tickLine: false, axisLine: false }), _jsx(ChartTooltip, { content: _jsx(ChartTooltipContent, {}) }), _jsx(Bar, { dataKey: "tokens", fill: "var(--color-tokens)", radius: [0, 3, 3, 0], ...staticChartProps }), _jsx(Bar, { dataKey: "messages", fill: "var(--color-messages)", radius: [0, 3, 3, 0], ...staticChartProps })] }) }) }));
|
|
207
|
+
}
|
|
198
208
|
export function ActivityHeatmap({ data }) {
|
|
199
209
|
return (_jsx(ChartPanel, { title: "Activity heatmap", description: "UTC weekday and hour density.", isEmpty: !hasPositiveValue(data, (item) => [item.commits]), emptyTitle: "No activity heatmap to show", emptyDescription: "The heatmap needs at least one dated commit.", children: _jsx("div", { className: "grid grid-flow-dense grid-cols-[repeat(24,minmax(0,1fr))] gap-1", "aria-label": "Activity heatmap by day and hour", children: data.map((cell) => (_jsx("div", { title: `${cell.day} ${cell.hour}: ${cell.commits} commits`, className: cn("h-4 rounded-[2px] border", heatClass(cell.commits)), children: _jsx("span", { className: "sr-only", children: `${cell.day} ${cell.hour}: ${cell.commits} commits` }) }, `${cell.day}-${cell.hour}`))) }) }));
|
|
200
210
|
}
|
|
211
|
+
export function ScanCommitBarChart({ data }) {
|
|
212
|
+
return (_jsx(ChartPanel, { title: "Commits per project", description: "Commit count across scanned repositories.", isEmpty: !hasPositiveValue(data, (item) => [item.commits]), emptyTitle: "No commits to chart", emptyDescription: "Commit comparison needs at least one scanned project with commits.", children: _jsx(ChartContainer, { config: { commits: { label: "Commits", color: chartPalette[1] } }, className: "h-64 w-full", children: _jsxs(BarChart, { data: data, layout: "vertical", margin: { left: 8, right: 16, top: 8, bottom: 0 }, children: [_jsx(CartesianGrid, { horizontal: false }), _jsx(XAxis, { type: "number", hide: true }), _jsx(YAxis, { dataKey: "name", type: "category", width: 112, tickLine: false, axisLine: false }), _jsx(ChartTooltip, { content: _jsx(ChartTooltipContent, {}) }), _jsx(Bar, { dataKey: "commits", fill: "var(--color-commits)", radius: [0, 3, 3, 0], ...staticChartProps })] }) }) }));
|
|
213
|
+
}
|
|
214
|
+
export function ScanChurnPieChart({ data }) {
|
|
215
|
+
return (_jsx(ChartPanel, { title: "Churn per project", description: "Lines changed (additions + deletions) by project.", isEmpty: !hasPositiveValue(data, (item) => [item.churn]), emptyTitle: "No churn to chart", emptyDescription: "Churn data appears after file-level additions or deletions exist.", children: _jsx(ChartContainer, { config: { churn: { label: "Churn" } }, className: "h-64 w-full", children: _jsxs(PieChart, { children: [_jsx(ChartTooltip, { content: _jsx(ChartTooltipContent, { nameKey: "name" }) }), _jsx(Pie, { data: data, dataKey: "churn", nameKey: "name", innerRadius: 56, outerRadius: 88, paddingAngle: 2, ...staticChartProps, children: data.map((slice, index) => (_jsx(Cell, { fill: chartPalette[index % chartPalette.length] }, slice.name))) })] }) }) }));
|
|
216
|
+
}
|
|
217
|
+
export function ScanAiMessagesPieChart({ data }) {
|
|
218
|
+
return (_jsx(ChartPanel, { title: "AI messages per project", description: "Message share across scanned repositories.", isEmpty: !hasPositiveValue(data, (item) => [item.messages]), emptyTitle: "No AI messages to chart", emptyDescription: "AI message data appears after local assistant records are matched to projects.", children: _jsx(ChartContainer, { config: { messages: { label: "Messages" } }, className: "h-64 w-full", children: _jsxs(PieChart, { children: [_jsx(ChartTooltip, { content: _jsx(ChartTooltipContent, { nameKey: "name" }) }), _jsx(Pie, { data: data, dataKey: "messages", nameKey: "name", innerRadius: 56, outerRadius: 88, paddingAngle: 2, ...staticChartProps, children: data.map((slice, index) => (_jsx(Cell, { fill: chartPalette[index % chartPalette.length] }, slice.name))) })] }) }) }));
|
|
219
|
+
}
|
|
220
|
+
export function ScanAiTokensBarChart({ data }) {
|
|
221
|
+
return (_jsx(ChartPanel, { title: "AI tokens per project", description: "Token usage across scanned repositories.", isEmpty: !hasPositiveValue(data, (item) => [item.tokens]), emptyTitle: "No AI tokens to chart", emptyDescription: "AI token data appears after local assistant records are matched to projects.", children: _jsx(ChartContainer, { config: { tokens: { label: "Tokens", color: chartPalette[2] } }, className: "h-64 w-full", children: _jsxs(BarChart, { data: data, layout: "vertical", margin: { left: 8, right: 16, top: 8, bottom: 0 }, children: [_jsx(CartesianGrid, { horizontal: false }), _jsx(XAxis, { type: "number", hide: true }), _jsx(YAxis, { dataKey: "name", type: "category", width: 112, tickLine: false, axisLine: false }), _jsx(ChartTooltip, { content: _jsx(ChartTooltipContent, {}) }), _jsx(Bar, { dataKey: "tokens", fill: "var(--color-tokens)", radius: [0, 3, 3, 0], ...staticChartProps })] }) }) }));
|
|
222
|
+
}
|
|
223
|
+
export function ScanAiModelsPieChart({ data }) {
|
|
224
|
+
return (_jsx(ChartPanel, { title: "AI usage by model", description: "Message share across all models used in the scan.", isEmpty: !hasPositiveValue(data, (item) => [item.messages]), emptyTitle: "No AI model data to chart", emptyDescription: "AI model data appears after local assistant records with model metadata are matched.", children: _jsx(ChartContainer, { config: { messages: { label: "Messages" } }, className: "h-64 w-full", children: _jsxs(PieChart, { children: [_jsx(ChartTooltip, { content: _jsx(ChartTooltipContent, { nameKey: "name" }) }), _jsx(Pie, { data: data, dataKey: "messages", nameKey: "name", innerRadius: 56, outerRadius: 88, paddingAngle: 2, ...staticChartProps, children: data.map((slice, index) => (_jsx(Cell, { fill: chartPalette[index % chartPalette.length] }, slice.name))) })] }) }) }));
|
|
225
|
+
}
|
|
226
|
+
export function deriveScanCommitData(projects) {
|
|
227
|
+
return projects
|
|
228
|
+
.map((project) => ({
|
|
229
|
+
name: project.repository.name,
|
|
230
|
+
commits: project.report.commits.length,
|
|
231
|
+
}))
|
|
232
|
+
.sort((left, right) => right.commits - left.commits || left.name.localeCompare(right.name));
|
|
233
|
+
}
|
|
234
|
+
export function deriveScanChurnData(projects) {
|
|
235
|
+
return projects
|
|
236
|
+
.map((project) => {
|
|
237
|
+
let additions = 0;
|
|
238
|
+
let deletions = 0;
|
|
239
|
+
for (const commit of project.report.commits) {
|
|
240
|
+
for (const file of commit.files) {
|
|
241
|
+
additions += file.additions;
|
|
242
|
+
deletions += file.deletions;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return { name: project.repository.name, additions, deletions, churn: additions + deletions };
|
|
246
|
+
})
|
|
247
|
+
.filter((item) => item.churn > 0)
|
|
248
|
+
.sort((left, right) => right.churn - left.churn || left.name.localeCompare(right.name));
|
|
249
|
+
}
|
|
250
|
+
export function deriveScanAiPerProjectData(projects) {
|
|
251
|
+
return projects
|
|
252
|
+
.filter((project) => project.report.aiUsage !== undefined && project.report.aiUsage.records > 0)
|
|
253
|
+
.map((project) => ({
|
|
254
|
+
name: project.repository.name,
|
|
255
|
+
messages: project.report.aiUsage.records,
|
|
256
|
+
tokens: project.report.aiUsage.tokens.total,
|
|
257
|
+
}))
|
|
258
|
+
.sort((left, right) => right.messages - left.messages || left.name.localeCompare(right.name));
|
|
259
|
+
}
|
|
260
|
+
export function deriveScanAiModelsData(aiUsage) {
|
|
261
|
+
return aiUsage.breakdowns.byModel
|
|
262
|
+
.filter((item) => item.records > 0 || item.tokens.total > 0)
|
|
263
|
+
.map((item) => ({
|
|
264
|
+
name: item.key,
|
|
265
|
+
messages: item.records,
|
|
266
|
+
tokens: item.tokens.total,
|
|
267
|
+
}))
|
|
268
|
+
.sort((left, right) => right.messages - left.messages || left.name.localeCompare(right.name));
|
|
269
|
+
}
|
|
201
270
|
function heatClass(commits) {
|
|
202
271
|
if (commits >= 8) {
|
|
203
272
|
return "border-chart-4/40 bg-chart-4";
|
package/dist/layout.js
CHANGED
|
@@ -6,13 +6,13 @@ import { EmptyState } from "./empty-state.js";
|
|
|
6
6
|
import { ThemeToggle } from "./theme-toggle.js";
|
|
7
7
|
export function Header({ title, titleHref, eyebrow, description, actions }) {
|
|
8
8
|
const titleContent = _jsx("h1", { className: "mt-3 text-3xl font-semibold tracking-tight text-foreground sm:text-4xl", children: title });
|
|
9
|
-
return (_jsx("header", { className: "border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/75", children: _jsxs("div", { className: "mx-auto flex w-full
|
|
9
|
+
return (_jsx("header", { className: "border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/75", children: _jsxs("div", { className: "mx-auto flex w-full flex-col gap-6 px-6 py-8 sm:px-10 lg:flex-row lg:items-end lg:justify-between", children: [_jsxs("div", { className: "max-w-4xl", children: [eyebrow ? _jsx("p", { className: "text-xs font-medium uppercase tracking-[0.24em] text-muted-foreground", children: eyebrow }) : null, titleHref ? (_jsx("a", { href: titleHref, target: "_blank", rel: "noopener noreferrer", className: "hover:underline", children: titleContent })) : (titleContent), description ? _jsx("p", { className: "mt-3 max-w-2xl text-sm leading-6 text-muted-foreground", children: description }) : null] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [actions, _jsx(ThemeToggle, {})] })] }) }));
|
|
10
10
|
}
|
|
11
11
|
export function Navigation({ items, label = "Report sections" }) {
|
|
12
12
|
if (items.length === 0) {
|
|
13
13
|
return null;
|
|
14
14
|
}
|
|
15
|
-
return (_jsx("nav", { "aria-label": label, className: "border-b bg-background/80", children: _jsx("div", { className: "mx-auto flex w-full
|
|
15
|
+
return (_jsx("nav", { "aria-label": label, className: "border-b bg-background/80", children: _jsx("div", { className: "mx-auto flex w-full gap-2 overflow-x-auto px-6 py-3 sm:px-10", children: items.map((item) => item.disabled ? (_jsx("span", { "aria-disabled": "true", className: "inline-flex h-8 shrink-0 items-center border border-transparent px-3 text-xs font-medium text-muted-foreground/60", children: item.label }, item.href)) : (_jsx("a", { href: item.href, "aria-current": item.current ? "page" : undefined, className: buttonVariants({ variant: item.current ? "secondary" : "ghost", size: "sm", className: "shrink-0" }), children: item.label }, item.href))) }) }));
|
|
16
16
|
}
|
|
17
17
|
export function StatsGrid({ stats, emptyTitle = "No report statistics yet", emptyDescription = "This report does not include enough data for summary statistics.", }) {
|
|
18
18
|
if (stats.length === 0) {
|
|
@@ -21,5 +21,5 @@ export function StatsGrid({ stats, emptyTitle = "No report statistics yet", empt
|
|
|
21
21
|
return (_jsx("section", { "aria-label": "Report summary statistics", className: "grid grid-flow-dense gap-3 sm:grid-cols-2 lg:grid-cols-4", children: stats.map((stat) => (_jsxs(Card, { className: "min-h-32 shadow-none", children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-xs font-medium uppercase tracking-[0.18em] text-muted-foreground", children: stat.label }) }), _jsxs(CardContent, { children: [_jsx("p", { className: "text-3xl font-semibold tracking-tight text-foreground", children: stat.value }), stat.description ? _jsx("p", { className: "mt-2 text-xs leading-5 text-muted-foreground", children: stat.description }) : null] })] }, stat.label))) }));
|
|
22
22
|
}
|
|
23
23
|
export function AppShell({ title, titleHref, eyebrow, description, navigationItems = [], headerActions, children, }) {
|
|
24
|
-
return (_jsxs("main", { className: "min-h-screen w-full max-w-full overflow-x-hidden bg-background text-foreground transition-colors", children: [_jsx(Header, { title: title, titleHref: titleHref, eyebrow: eyebrow, description: description, actions: headerActions }), _jsx(Navigation, { items: navigationItems }), _jsx("div", { className: cn("mx-auto flex w-full
|
|
24
|
+
return (_jsxs("main", { className: "min-h-screen w-full max-w-full overflow-x-hidden bg-background text-foreground transition-colors", children: [_jsx(Header, { title: title, titleHref: titleHref, eyebrow: eyebrow, description: description, actions: headerActions }), _jsx(Navigation, { items: navigationItems }), _jsx("div", { className: cn("mx-auto flex w-full flex-col gap-8 px-6 py-8 sm:px-10 sm:py-10"), children: children })] }));
|
|
25
25
|
}
|