Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .agents/skills/update-samples/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Tags are automatically inferred from the README content and sample name. The scr
| `javascript` | JavaScript references |
| `node` | Node.js references |
| `go` | Go/Golang references |
| `java` | Java, Spring Boot, Quarkus, Maven, Gradle references |

#### Services & Technologies
| Tag | Matched by |
Expand Down
4 changes: 4 additions & 0 deletions src/frontend/scripts/update-samples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ const TAG_RULES: TagRule[] = [
patterns: [/\bTypeScript\b/i, /\bts-node\b/i, /\bapphost\.m?ts\b/i, /\.m?tsx?\b/],
},
{ tag: 'node', patterns: [/\bNode\.?js\b/i, /\bnpm\b/i] },
{
tag: 'java',
patterns: [/\bJava\b/i, /\bSpring\s+Boot\b/i, /\bQuarkus\b/i, /\bMaven\b/i, /\bGradle\b/i, /\bmvnw\b/i],
},
{
tag: 'go',
patterns: [
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
74 changes: 59 additions & 15 deletions src/frontend/src/data/samples.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/frontend/src/utils/sample-tags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const TAG_LABELS: Record<string, string> = {
'typescript': 'TypeScript',
'node': 'Node.js',
'go': 'Go',
'java': 'Java',
'redis': 'Redis',
'postgresql': 'PostgreSQL',
'sql-server': 'SQL Server',
Expand Down
31 changes: 30 additions & 1 deletion src/frontend/src/utils/samples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,41 @@ export function sampleDetailHref(base: string, name: string): string {
return `${normalizedBase}/reference/samples/${sampleSlug(name)}/`;
}

/** Matches a Markdown table separator row, with or without leading/trailing pipes (e.g. `| --- | --- |`, `--- | ---`, `:--|--:`). */
const TABLE_SEPARATOR_ROW = /^\s*\|?\s*:?-{2,}:?\s*(\|\s*:?-{2,}:?\s*)+\|?\s*$/;

/** Strips whole Markdown table blocks (header, separator, and body rows), tolerating missing leading/trailing pipes. */
function stripMarkdownTables(text: string): string {
const lines = text.split('\n');
const result: string[] = [];

for (let i = 0; i < lines.length; i++) {
if (TABLE_SEPARATOR_ROW.test(lines[i])) {
// Drop the header row that precedes the separator, if one was just pushed.
if (result.length > 0 && result[result.length - 1].includes('|')) {
result.pop();
}
// Skip the separator and any following body rows that still look like table rows.
i++;
while (i < lines.length && lines[i].includes('|')) {
i++;
}
i--;
continue;
}

result.push(lines[i]);
}

return result.join('\n');
}

export function sampleDescriptionText(description: string | null): string | null {
if (!description) {
return null;
}

const text = description
const text = stripMarkdownTables(description)
.replace(/!\[([^\]]*)\]\([^)]+\)/g, '$1')
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
.replace(/`([^`]+)`/g, '$1')
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/tests/e2e/featured-samples.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ test('featured samples filter real metadata and retain language when browsing al
const languages = samples.getByRole('group', { name: 'Sample languages' });
const visible = samples.locator('[data-sample-languages]:visible h3');
await expect(samples).toHaveAttribute('data-ready', '');
await expect(languages.getByRole('checkbox')).toHaveCount(5);
await expect(languages.getByRole('checkbox')).toHaveCount(6);
await expect(languages.getByRole('checkbox', { checked: true })).toHaveCount(0);
await expect(visible).toHaveCount(6);
await expect(samples.getByRole('status')).toHaveText('6 featured samples: All languages');
Expand Down
28 changes: 28 additions & 0 deletions src/frontend/tests/unit/custom-components.vitest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1450,6 +1450,34 @@ describe('custom Astro component render coverage', () => {
expect(html).toContain('Zoom image: Aspire dashboard');
});

it('drops markdown tables from plain-text sample descriptions', async () => {
const { sampleDescriptionText } = await import('@utils/samples');

expect(
sampleDescriptionText(
'Intro paragraph.\n\n| Sample | AppHost |\n| --- | --- |\n| [C# basics](./basics/) | C# |\n\nClosing paragraph.'
)
).toBe('Intro paragraph.\n\nClosing paragraph.');
Comment thread
IEvangelist marked this conversation as resolved.
});

it('drops markdown tables missing leading/trailing pipes', async () => {
const { sampleDescriptionText } = await import('@utils/samples');

expect(
sampleDescriptionText('Intro paragraph.\n\nSample | AppHost\n--- | ---\nC# basics | C#\n\nClosing paragraph.')
).toBe('Intro paragraph.\n\nClosing paragraph.');
});

it('drops markdown tables with alignment markers', async () => {
const { sampleDescriptionText } = await import('@utils/samples');

expect(
sampleDescriptionText(
'Intro paragraph.\n\n| Sample | AppHost |\n|:---|---:|\n| C# basics | C# |\n\nClosing paragraph.'
)
).toBe('Intro paragraph.\n\nClosing paragraph.');
});

it('builds sample markdown payload with absolute image URLs and metadata preamble', async () => {
const { appHostLabel, buildSampleMarkdown } = await import('@utils/samples');

Expand Down
Loading