Skip to content
Open
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
7 changes: 4 additions & 3 deletions .agents/skills/whatsnew/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ infer from context — e.g. a fresh release with no page → `draft`).

| Modality | Alias | Purpose | Spec | Edits content? |
|----------|-------|---------|------|----------------|
| `draft` | `scaffold`, `init` | Stand up branch, draft PR, skeleton MDX, banner, sidebar, version constants. **Structure only.** | [`01-draft-scaffold.md`](./references/01-draft-scaffold.md) | Skeleton only |
| `draft` | `scaffold`, `init` | Stand up branch, draft PR, skeleton MDX, release badge, banners, sidebar, version constants. **Structure only.** | [`01-draft-scaffold.md`](./references/01-draft-scaffold.md) | Skeleton only |
| `research` | — | Enumerate every change into an impact-focused **dossier**, then **author the first-draft prose** into the MDX from it. | [`02-research.md`](./references/02-research.md) | **Yes — drafts the body** |
| `critique` | — | Content-quality **self-review** of draft vs dossier; severity-ranked findings. | [`03-critique.md`](./references/03-critique.md) | **No — reports only** |
| `validate` | — | **Ralph loop** with `doc-pr-reviewer` until 100% factual; samples/links/assets. **No `bun run build`.** | [`04-validate.md`](./references/04-validate.md) | Fixes only |
Expand Down Expand Up @@ -86,9 +86,10 @@ infer from context — e.g. a fresh release with no page → `draft`).
| What | Path |
|------|------|
| What's-new pages | `src/frontend/src/content/docs/whats-new/aspire-N-N.mdx` (JA under `.../ja/whats-new/`) |
| Version constants | `src/frontend/config/aspire-versions.mjs` (`currentAspireMajorMinorVersion`, `currentAspireVersion`, `currentAspireReleaseDate`, `currentAspirePreviewVersion`) |
| Version constants | `src/frontend/config/aspire-versions.mjs` (`currentAspireMajorMinorVersion`, `currentAspireVersion`, `currentAspireReleaseDate`, `latestAspireReleaseVersion`, `currentAspirePreviewVersion`) |
| Homepage release badge | `src/frontend/src/components/home/HomeHero.astro` (reads `latestAspireReleaseVersion` and links to the exact `microsoft/aspire` GitHub release tag) |
| Sidebar | `src/frontend/config/sidebar/docs.topics.ts` (What's-new `items`) |
| Announcement banner | `banner:` frontmatter on `src/frontend/src/content/docs/index.mdx`, `src/frontend/src/content/docs/docs.mdx`, `src/frontend/src/content/docs/community/index.mdx`, and each `src/frontend/src/content/docs/{locale}/index.mdx` (+ `.../ja/docs.mdx`); rendered by `src/frontend/src/components/starlight/Banner.astro` |
| Announcement banner | `banner:` frontmatter on `src/frontend/src/content/docs/docs.mdx`, `src/frontend/src/content/docs/community/index.mdx`, each localized `src/frontend/src/content/docs/{locale}/index.mdx`, and `src/frontend/src/content/docs/ja/docs.mdx`; rendered by `src/frontend/src/components/starlight/Banner.astro` |
| Assets | `src/frontend/src/assets/whats-new/aspire-<version>/` |
| Container image data | `src/frontend/src/data/container-images.json` |
| Diagnostics articles | `src/frontend/src/content/docs/diagnostics/aspire<area><NNN>.mdx` → `/diagnostics/aspire<NNN>/` |
Expand Down
45 changes: 27 additions & 18 deletions .agents/skills/whatsnew/references/01-draft-scaffold.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
> Aliases: `draft`, `scaffold`, `init`. **Structure only — no researched content.**

Stand up everything a new "What's new in Aspire N.N" page needs so the team can
start iterating: the release branch, the skeleton MDX, the site banner, the sidebar
entry, and the version constants — all **committed**, and only then a **draft PR**.
start iterating: the release branch, the skeleton MDX, the homepage release badge,
the site banners, the sidebar entry, and the version constants — all **committed**,
and only then a **draft PR**.
Idempotent — safe to re-run; detect and update what already exists instead of
duplicating.

Expand Down Expand Up @@ -45,33 +46,40 @@ duplicating.
The resource directory automatically includes the page as `release-notes` and
reads its image and `publishDate`; no separate catalog entry is needed.
3. **Version constants.** Update `src/frontend/config/aspire-versions.mjs`:
`currentAspireMajorMinorVersion = 'N.N'`, `currentAspireVersion` to the latest
stable `N.N.PATCH`, `currentAspireReleaseDate` to that patch's NuGet publish
date (`YYYY-MM-DD`, shown in the Install CLI dialog), and `currentAspirePreviewVersion` to the matching full
`N.N.PATCH-preview.*` package version. Set both package versions to the initial
release patch when N.N becomes current, then keep them aligned with the site
AppHost during servicing updates. These drive the Aspire version remark
placeholders site-wide.
4. **Site-wide announcement banner.** The banner is **per-page frontmatter**, not a
- Set `currentAspireMajorMinorVersion = 'N.N'`.
- Keep `currentAspireVersion` and `currentAspireReleaseDate` aligned with the
stable SDK/package version used by the site AppHost. This is not necessarily
the latest public GitHub patch release.
- Set `latestAspireReleaseVersion` to the latest public `N.N.PATCH` release tag.
The homepage badge reads this value and links to the exact
`https://github.com/microsoft/aspire/releases/tag/vN.N.PATCH` release.
- Set `currentAspirePreviewVersion` to the matching full
`N.N.PATCH-preview.*` package version used by the site AppHost.

The AppHost-aligned constants drive version placeholders and the Install CLI
dialog. `latestAspireReleaseVersion` independently drives the homepage badge.
4. **Homepage release badge.** Do not hand-edit badge markup or copy. Confirm
`latestAspireReleaseVersion` renders as the full point release and that the badge
links to its exact `microsoft/aspire` GitHub release tag and `/support/`.
5. **Site-wide announcement banners.** The banner is **per-page frontmatter**, not a
global config. Update the `banner.content` string **and its link** to point at
`/whats-new/{SLUG}/` across every landing page that carries it, and set
`bannerAutoDismissAfterDays: 14` (and `bannerExpiresOn` if used):
- English: `src/frontend/src/content/docs/index.mdx`, `docs.mdx`,
`community/index.mdx`.
- English: `src/frontend/src/content/docs/docs.mdx` and `community/index.mdx`.
- Every locale: `src/frontend/src/content/docs/{locale}/index.mdx` (+
`ja/docs.mdx`). Translate the announcement text per locale and point at the
locale-appropriate whats-new path. (Grep `banner:` under
`src/frontend/src/content/docs/` to enumerate current banner pages.)
5. **Sidebar entry.** In `src/frontend/config/sidebar/docs.topics.ts`, add
6. **Sidebar entry.** In `src/frontend/config/sidebar/docs.topics.ts`, add
`{ label: 'Aspire N.N', slug: 'whats-new/{SLUG}' }` at the **top** of the What's-new
`items`. Roll the now-older version into the "Previous versions" group per the
existing pattern.
6. **Assets.** Create `src/frontend/src/assets/whats-new/aspire-{VERSION_FULL}/`
7. **Assets.** Create `src/frontend/src/assets/whats-new/aspire-{VERSION_FULL}/`
only when adding actual screenshots. Do not create an empty directory or
`.gitkeep`; reused shared artwork needs no release-specific folder.
7. **Commit the scaffold.** Stage and commit all of the above on `release/{N.N}` so the
8. **Commit the scaffold.** Stage and commit all of the above on `release/{N.N}` so the
branch has a diff against the default branch (nothing to PR otherwise).
8. **Draft PR.** *Now* open a **draft** PR from `release/{N.N}` into the repository's
9. **Draft PR.** *Now* open a **draft** PR from `release/{N.N}` into the repository's
**default branch** for the team to iterate on. Create it if missing; don't mark it
ready here (that's {review}).

Expand All @@ -83,9 +91,10 @@ duplicating.
## Exit criteria

- Page exists and is structurally sound (frontmatter + imports + section skeleton).
- Sidebar resolves the new slug; banner points at the new article across locales.
- Sidebar resolves the new slug; the homepage badge shows the full point release
and links to the exact GitHub release; banners point at the new article across locales.
- Release-specific screenshots exist and `resourceImage` resolves to them, or
their capture is explicitly tracked as incomplete. The resource directory
discovers one release-notes entry with the expected metadata.
- Version constants updated; draft PR is open.
- AppHost and latest-release version constants updated; draft PR is open.
- **No researched prose yet** — only structure and placeholders.
8 changes: 5 additions & 3 deletions .agents/skills/whatsnew/references/04-validate.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ then **fix → re-review → repeat** until it converges clean:
image/asset reference exists. For links **into `release/{N.N}`** that may not be
merged yet: **block on truly-broken**, **warn on not-yet-merged `docs-from-code`**
targets.
- **Banner + version wiring:** banner points at the new article across locales; version
constants in `aspire-versions.mjs` are correct.
- **Release badge + banner + version wiring:** the homepage badge shows
`latestAspireReleaseVersion`, links to the exact `microsoft/aspire` GitHub release
tag and `/support/`, banners point at the new article across locales, and
AppHost-aligned version constants remain correct.
- **Resource directory:** `resourceImage` is present and every configured image
resolves to a newly captured screenshot of a feature introduced in this release.
Verify capture provenance (product version, scenario, and actual UI state);
Expand All @@ -58,4 +60,4 @@ good technical read.
- `doc-pr-reviewer` converges with **no** factual findings.
- All `twoslash` samples pass; all links/assets resolve (or are explicitly
warn-listed as pending `docs-from-code`).
- MDX/frontmatter valid; sidebar/banner/version wiring correct.
- MDX/frontmatter valid; sidebar/release-badge/banner/version wiring correct.
4 changes: 2 additions & 2 deletions .agents/skills/whatsnew/references/05-polish.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ handoff. **Re-run [`{validate}`](./04-validate.md)** on the changed article and

- `doc-pr-reviewer` surfaces **no** new factual findings on the edited prose.
- `twoslash-validator` passes on any changed/regenerated `twoslash` blocks.
- MDX/frontmatter still valid; all links/assets still resolve; sidebar/banner/version
wiring intact.
- MDX/frontmatter still valid; all links/assets still resolve; sidebar/release-badge/
banner/version wiring intact.

`{polish}` is **not done** until this final `{validate}` pass is green (`{review}`
re-confirms it).
Expand Down
1 change: 1 addition & 0 deletions src/frontend/config/aspire-versions.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export const currentAspireMajorMinorVersion = '13.6';
export const currentAspireVersion = '13.6.0';
// Release date (YYYY-MM-DD) of currentAspireVersion; update together with the version.
export const currentAspireReleaseDate = '2026-09-29';
export const latestAspireReleaseVersion = '13.6.4';
export const currentAspirePreviewVersion = '13.6.0-preview.1.26479.8';

export const aspireVersionPlaceholders = Object.freeze({
Expand Down
109 changes: 88 additions & 21 deletions src/frontend/src/components/home/HomeHero.astro
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import heroImage from '@assets/aspire-hero.png';
import AppHostBuilder from '@components/AppHostBuilder.astro';
import { localizedHref } from '@utils/helpers';
import { getLocaleFromPath } from '@utils/locale-routes';
import { latestAspireReleaseVersion } from '../../../config/aspire-versions.mjs';

const t = Astro.locals.t;
const locale = getLocaleFromPath(Astro.url.pathname);
const releaseHref = `https://github.com/microsoft/aspire/releases/tag/v${latestAspireReleaseVersion}`;
---

<section
Expand All @@ -21,10 +23,22 @@ const locale = getLocaleFromPath(Astro.url.pathname);
<div class="home-hero-grid" aria-hidden="true"></div>

<div class="home-hero-copy">
<p class="home-hero-eyebrow">
<span aria-hidden="true"></span>
{t('home.hero.eyebrow')}
</p>
<nav class="home-hero-release" aria-label={`Aspire ${latestAspireReleaseVersion}`}>
<a
class="home-release-link"
href={releaseHref}
target="_blank"
rel="noopener noreferrer"
aria-label={`Aspire ${latestAspireReleaseVersion}`}
title={`View the Aspire ${latestAspireReleaseVersion} release on GitHub`}
data-tooltip-placement="top"
data-tippy-allowhtml="false"
>
<span class="home-release-version">v{latestAspireReleaseVersion}</span>
<Icon name="external" class="home-release-external" size="0.7rem" />
</a>
<a class="home-release-support" href="/support/">{t('footer.support')}</a>
</nav>
<h1 id="_top" set:html={t('home.hero.title')} />
<p class="home-hero-summary">
{t('home.hero.summary')}
Expand Down Expand Up @@ -231,23 +245,77 @@ const locale = getLocaleFromPath(Astro.url.pathname);
will-change: transform, opacity;
}

.home-hero-eyebrow {
.home-hero-release {
display: inline-flex;
align-items: center;
gap: 0.65rem;
gap: 0.125rem;
margin: 0 0 1.25rem;
padding: 0.125rem;
overflow: hidden;
border: 0.0625rem solid color-mix(in srgb, var(--home-purple) 24%, var(--home-border));
border-radius: 999rem;
color: var(--home-muted);
font: 600 0.76rem/1.2 var(--__sl-font-mono);
letter-spacing: 0.12em;
text-transform: uppercase;
background: color-mix(in srgb, var(--home-purple) 11%, var(--home-surface));
box-shadow:
inset 0 0.0625rem 0 color-mix(in srgb, var(--home-surface) 88%, transparent),
0 0.3rem 0.9rem color-mix(in srgb, var(--home-purple) 7%, transparent);
font-size: 0.625rem;
font-weight: 650;
line-height: 1;
}

.home-hero-eyebrow span {
width: 0.55rem;
height: 0.55rem;
border-radius: 50%;
background: #4cb782;
box-shadow: 0 0 0 0.3rem rgba(76, 183, 130, 0.14);
.home-hero-release a {
box-sizing: border-box;
height: 1.375rem;
display: inline-flex;
align-items: center;
gap: 0.25rem;
padding: 0.3125rem 0.5rem;
color: inherit;
text-decoration: none;
}

.home-hero-release .home-release-link {
color: var(--home-purple);
}

.home-release-version {
position: relative;
top: 0.0625rem;
font-family: var(--__sl-font-mono);
font-weight: 750;
}

.home-release-link :global(.home-release-external) {
flex-shrink: 0;
}

.home-hero-release .home-release-support {
height: 1.375rem;
padding: 0.25rem 0.5rem;
border: 0.0625rem solid color-mix(in srgb, var(--home-purple) 20%, var(--home-border));
border-radius: 999rem;
color: var(--home-muted);
background: color-mix(in srgb, var(--home-surface) 90%, transparent);
box-shadow:
inset 0 0.0625rem 0 color-mix(in srgb, var(--home-surface) 95%, transparent),
0 0.0625rem 0.125rem color-mix(in srgb, var(--home-purple) 10%, transparent);
font-size: 0.625rem;
font-weight: 500;
}

.home-hero-release .home-release-link:hover {
color: var(--home-purple-bright);
}

.home-hero-release .home-release-support:hover {
color: var(--home-purple);
border-color: color-mix(in srgb, var(--home-purple) 42%, var(--home-border));
}

.home-hero-release a:focus-visible {
outline: 0.1875rem solid var(--aspire-action-focus, var(--home-purple));
outline-offset: -0.1875rem;
}

.home-hero-copy h1 {
Expand Down Expand Up @@ -545,10 +613,9 @@ const locale = getLocaleFromPath(Astro.url.pathname);
padding-top: 2rem;
}

.home-hero-eyebrow {
margin-bottom: 1rem;
.home-hero-release {
margin-bottom: 1.125rem;
font-size: var(--aspire-text-meta);
letter-spacing: 0.08em;
}

.home-hero-summary {
Expand Down Expand Up @@ -609,6 +676,7 @@ const locale = getLocaleFromPath(Astro.url.pathname);
.home-hero-action:hover {
transform: none;
}

}

@media (forced-colors: active) {
Expand All @@ -624,9 +692,8 @@ const locale = getLocaleFromPath(Astro.url.pathname);
background: ButtonFace;
}

.home-hero-eyebrow span {
background: Highlight;
box-shadow: none;
.home-hero-release {
border-color: CanvasText;
}
}
</style>
4 changes: 0 additions & 4 deletions src/frontend/src/content/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ template: splash
editUrl: false
prev: false
next: false
banner:
content: |
<strong>✨ Aspire 13.6 is available!</strong> — <a href="/whats-new/aspire-13-6/">Explore the latest features and improvements</a>
bannerAutoDismissAfterDays: 14
hero:
title: Compose distributed apps in code.
tagline: Model, run, observe, and deploy distributed applications from one code-first control plane.
Expand Down
4 changes: 2 additions & 2 deletions src/frontend/src/content/docs/support.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ editUrl: false

import { Badge } from '@astrojs/starlight/components';

<Badge text="📆 Last updated: October 20, 2026" variant="tip" size="large" />
<Badge text="📆 Last updated: October 24, 2026" variant="tip" size="large" />

## Microsoft support for Aspire

Expand All @@ -27,7 +27,7 @@ The following table lists Aspire versions that are actively supported:

| Version | Original release date | Latest patch version | Patch release date | End of support |
| ----------- | --------------------- | -------------------- | ------------------ | -------------------------------------------- |
| Aspire 13.6 | September 29, 2026 | 13.6.2 | October 20, 2026 | When next major or minor version is released. |
| Aspire 13.6 | September 29, 2026 | 13.6.4 | October 24, 2026 | When next major or minor version is released. |

### Out of support versions

Expand Down
9 changes: 5 additions & 4 deletions src/frontend/tests/e2e/banner.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { expect, test, type Page } from '@playwright/test';

const MS_PER_DAY = 86_400_000;
const BANNER_PAGE = '/docs/';

/**
* Abort every script request so only inline scripts run. Anything the banner does after that is
Expand Down Expand Up @@ -28,13 +29,13 @@ test.describe('announcement banner', () => {
});

test('is in the first paint, so showing it cannot shift the page', async ({ page }) => {
await page.goto('/');
await page.goto(BANNER_PAGE);

await expect(page.locator('[data-aspire-banner]')).toBeVisible();
});

test('stays hidden for a reader who dismissed it', async ({ page }) => {
await page.goto('/');
await page.goto(BANNER_PAGE);
const { dismissKey } = await bannerKeys(page);

await page.evaluate((key) => localStorage.setItem(key, 'true'), dismissKey);
Expand All @@ -44,7 +45,7 @@ test.describe('announcement banner', () => {
});

test('stays hidden once its auto-dismiss window has elapsed', async ({ page }) => {
await page.goto('/');
await page.goto(BANNER_PAGE);
const { firstSeenKey } = await bannerKeys(page);

await page.evaluate(
Expand All @@ -57,7 +58,7 @@ test.describe('announcement banner', () => {
});

test('stays visible inside its auto-dismiss window', async ({ page }) => {
await page.goto('/');
await page.goto(BANNER_PAGE);
const { firstSeenKey } = await bannerKeys(page);

await page.evaluate(
Expand Down
Loading
Loading