docs: add automated what's new highlights (#25729)

## Summary

Replace the static Featured topics section with a data-driven Whatʼs new
timeline. Show the five most important launches by default and let
readers expand the full curated 30-day archive.

Run a daily Claude Sonnet 5 curator over a rolling 30-day window, with
manual dispatch support, deterministic validation, and bot-managed pull
requests. Package the editorial workflow as the reusable
`curate-whats-new` repository skill so Docker Agent and other Agent
Skills-compatible clients can invoke the same process.

Preserve the human-reviewed July 13 through August 11, 2026 result and
flexible selection criteria as a regression example outside the skillʼs
runtime context.

@netlify /

[Homepage
preview](https://deploy-preview-25729--docsdocker.netlify.app/)

Generated by Codex
This commit is contained in:
David Karlsson authored and GitHub committed 2026-08-12 17:23:31 +02:00
1 parent 58f1389442
commit 678542f456
12 files changed
+685 -45

No files matched your search

+74
View File
@@ -0,0 +1,74 @@
---
name: curate-whats-new
description: Curate noteworthy Docker launches from documentation pull requests merged during a requested period. Use when generating or reviewing data/whats-new.json, preparing the Docker Docs What's new timeline, or deciding which documented releases merit Docker-wide highlights.
---
# Curate What's New
Treat merged documentation as evidence that a capability shipped, but do not
treat a documentation change as news by itself.
Include an item only when the merged documentation directly shows all of the
following:
- A released user-facing feature, material enhancement, or broader availability
milestone that was not available before the period
- A substantial capability or workflow, not new syntax or a small control
within an existing workflow
- Enough Docker-wide editorial significance to merit proactively telling users
about it outside product release notes
- A useful published page and a factual title and description
Apply a high bar. The result is a curated launch archive, not a complete
changelog. A specialized feature can qualify when its user impact is
substantial. A quiet period can produce few or no items.
## Exclusions
Exclude documentation maintenance; fixes; rewrites; guidance for old behavior;
routine release or generated-content syncs; limitations, prerequisites, and
workarounds; narrow flags, settings, command variants, protocols, and
compatibility changes; incremental UI, safety, permissions, or observability
improvements; and lower-level Engine, Build, networking, or storage changes.
These qualify only when they are part of an independently newsworthy
product-level launch.
Judge the user outcome, not PR size, product popularity, labels, changed lines,
a dedicated page, or the existence of a new API or command.
## Select highlights
Include every qualifying launch; do not impose a quota. Mark the five most
important as `featured: true`, or all items when fewer than five qualify. Rank
by the magnitude and distinctness of the user outcome and the value of helping
its audience discover it. Breadth can matter, but a major capability for a
specialized audience can outrank a smaller change for a broad audience. Recency
and product variety are not ranking goals.
Create one item per launch and combine PRs that document the same launch.
Preserve existing copy while it remains accurate and qualifies. Change featured
status only when the relative importance of the candidate set changes.
## Procedure
1. Read `data/whats-new.json`.
2. List every PR merged in the requested period:
```console
$ gh pr list --repo docker/docs --state merged --search 'merged:START..END' --limit 200
```
3. Inspect the diff and resulting pages for every plausible candidate.
4. Decide what qualifies using only evidence in the merged documentation.
5. Replace `period_start`, `period_end`, and `items` in
`data/whats-new.json`. Sort items by `published` date, newest first.
6. Write `.pr-body.md` with the publication period, selected highlights and
source PRs, plus concise reasons for plausible exclusions.
Each item must contain `product`, `title`, `description`, `url`, `published`,
`source_prs`, and `featured`. Use the canonical product name, a published
internal URL, the merge date in `YYYY-MM-DD` format, and source PR numbers.
Write factual, restrained copy. Avoid superlatives, promotional language, and
claims about ease or importance. Do not modify tracked files other than
`data/whats-new.json`.
@@ -0,0 +1,4 @@
interface:
display_name: "Curate What's New"
short_description: "Curate noteworthy Docker launches from merged docs"
default_prompt: "Use $curate-whats-new to curate Docker launches published during the requested date range."
@@ -0,0 +1,96 @@
{
"period_start": "2026-07-13",
"period_end": "2026-08-11",
"items": [
{
"product": "Docker Desktop",
"title": "Use Docker VMM on Mac and Windows",
"description": "Run Docker Desktop with Docker's container-optimized hypervisor on supported Mac and Windows systems.",
"url": "/desktop/features/vmm/",
"published": "2026-08-10",
"source_prs": [25702, 25726],
"featured": true
},
{
"product": "Docker Sandboxes",
"title": "Connect sandboxes through an MCP gateway",
"description": "Register local or remote MCP servers on the host, reuse them across sandboxes, and govern server and tool access with Cedar policies.",
"url": "/ai/sandboxes/mcp-gateway/",
"published": "2026-08-06",
"source_prs": [25559, 25707],
"featured": true
},
{
"product": "Docker Sandboxes",
"title": "Author version 2 sandbox kits",
"description": "Define kit setup, permissions, networking, and API key or OAuth credential requirements with the version 2 kit schema.",
"url": "/ai/sandboxes/customize/kit-reference/#schema-versions",
"published": "2026-08-06",
"source_prs": [25467, 25707],
"featured": false
},
{
"product": "Docker Sandboxes",
"title": "Run Claude Code with local models",
"description": "Route Claude Code sandbox requests to the bundled local-model server or an existing Ollama installation.",
"url": "/ai/sandboxes/agents/claude-code/#use-a-local-model",
"published": "2026-08-06",
"source_prs": [25641, 25707],
"featured": false
},
{
"product": "Docker AI Governance",
"title": "Search and forward audit events",
"description": "Search and export policy decisions from Docker Cloud, or forward events to Splunk, Dynatrace, and custom HTTPS endpoints.",
"url": "/ai/sandboxes/governance/audit/",
"published": "2026-08-03",
"source_prs": [25679, 25687],
"featured": true
},
{
"product": "Docker Desktop",
"title": "Install from the Microsoft Store without administrator privileges",
"description": "Fresh Microsoft Store installations on Windows use per-user mode by default.",
"url": "/desktop/release-notes/#4850",
"published": "2026-08-03",
"source_prs": [25665],
"featured": false
},
{
"product": "Docker Sandboxes",
"title": "Share agent skills across sandboxes",
"description": "Import skills from supported host agents into a persistent store that sandboxes can share.",
"url": "/ai/sandboxes/workflows/#share-agent-skills",
"published": "2026-07-24",
"source_prs": [25588],
"featured": false
},
{
"product": "Docker Sandboxes",
"title": "Connect editors and desktop apps over SSH",
"description": "Use a sandbox from VS Code, Cursor, Claude Desktop, ChatGPT, or another SSH-capable tool.",
"url": "/ai/sandboxes/integrations/",
"published": "2026-07-24",
"source_prs": [25557],
"featured": true
},
{
"product": "Docker Hardened Images",
"title": "Query the DHI catalog from AI assistants",
"description": "Search DHI repositories, inspect images, retrieve SBOMs, check CVEs, and manage mirrors through the DHI MCP server.",
"url": "/dhi/tools/mcp/",
"published": "2026-07-23",
"source_prs": [25589],
"featured": false
},
{
"product": "Docker Enterprise",
"title": "Authenticate GitHub Actions with OIDC connections",
"description": "Exchange GitHub OIDC tokens for short-lived access to Docker resources without storing long-lived workflow credentials.",
"url": "/enterprise/security/oidc-connections/",
"published": "2026-07-21",
"source_prs": [25336],
"featured": true
}
]
}
@@ -0,0 +1,39 @@
# Human-reviewed example and regression criteria for the curate-whats-new skill.
# This file is not loaded into the skill's runtime context.
period_start: 2026-07-13
period_end: 2026-08-11
example_output: 2026-07-13--2026-08-11.json
expected_archive:
- Docker VMM on Mac and Windows
- Docker Sandboxes MCP gateway
- Docker Sandboxes kit schema version 2
- Claude Code with local models
- Docker AI Governance audit search, export, and forwarding
- Microsoft Store per-user installation
- Shared agent skills across sandboxes
- Docker Sandboxes editor and app integrations
- Docker Hardened Images MCP server
- Enterprise OIDC connections
featured_guidance:
strong:
- Docker VMM on Mac and Windows
- Docker AI Governance audit search, export, and forwarding
- Docker Sandboxes editor and app integrations
- Enterprise OIDC connections
either_is_reasonable:
- Docker Sandboxes MCP gateway
- Docker Sandboxes kit schema version 2
must_exclude:
- Gordon safety modes and custom rules
- Swarm direct server return networking
- Engine image mounts
- Unix sockets on Windows
- Narrow flags, command variants, protocol options, and compatibility switches
notes:
- Do not require product diversity.
- Treat featured choices as editorial judgment, not an exact golden ordering.
- Reject lower-level or incremental changes even when they are technically new.
+34
View File
@@ -0,0 +1,34 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/docker/docker-agent/refs/heads/main/agent-schema.json
models:
claude-sonnet-5:
provider: anthropic
model: claude-sonnet-5
max_tokens: 4096
temperature: 0.1
agents:
root:
model: claude-sonnet-5
description: Curates user-facing Docker launches from recently published documentation
instruction: |
Use the curate-whats-new skill for every request.
skills:
- curate-whats-new
toolsets:
- type: filesystem
tools:
- read_file
- read_multiple_files
- write_file
- list_directory
- directory_tree
- type: shell
permissions:
allow:
- shell:cmd=gh pr list --*
- shell:cmd=gh pr view --*
- shell:cmd=gh pr diff --*
- shell:cmd=git diff --*
- shell:cmd=git log --*
+160
View File
@@ -0,0 +1,160 @@
name: Update What's New
on:
schedule:
# Review the previous 30 complete UTC days every day at 06:00 UTC.
- cron: "0 6 * * *"
workflow_dispatch:
inputs:
dry-run:
description: "Propose highlights without opening a pull request"
type: boolean
default: false
permissions:
contents: write
pull-requests: write
id-token: write
concurrency:
group: update-whats-new
cancel-in-progress: false
env:
BRANCH_NAME: bot/update-whats-new
jobs:
update:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
with:
fetch-depth: 0
- name: Set publication period
id: period
run: |
PERIOD_END=$(date -u -d 'yesterday' +%F)
echo "start=$(date -u -d "$PERIOD_END - 29 days" +%F)" >> "$GITHUB_OUTPUT"
echo "end=$PERIOD_END" >> "$GITHUB_OUTPUT"
- name: Configure AWS credentials
id: aws-credentials
continue-on-error: true
uses: aws-actions/configure-aws-credentials@d979d5b3a71173a29b74b5b88418bfda9437d885 # v6.1.1
with:
role-to-assume: arn:aws:iam::710015040892:role/docker-agent-action-20260409141318957000000001
aws-region: us-east-1
- name: Fetch bot token
if: steps.aws-credentials.outcome == 'success'
run: |
PAT=$(aws secretsmanager get-secret-value \
--secret-id docker-agent-action/github-app \
--query SecretString \
--output text | jq -r '.pat')
echo "::add-mask::$PAT"
echo "GITHUB_APP_TOKEN=$PAT" >> "$GITHUB_ENV"
- name: Restore open proposal
env:
GH_TOKEN: ${{ env.GITHUB_APP_TOKEN || github.token }}
run: |
EXISTING_PR=$(gh pr list --state open --head "$BRANCH_NAME" --json number --jq '.[0].number // empty')
if [ -n "$EXISTING_PR" ]; then
git fetch origin "$BRANCH_NAME:refs/remotes/origin/$BRANCH_NAME"
git show "origin/$BRANCH_NAME:data/whats-new.json" > data/whats-new.json
fi
- name: Curate highlights
uses: docker/docker-agent-action@e96a4bb40cac114f64358621e1d08346c8eadc8c # v2.0.1
env:
GH_TOKEN: ${{ env.GITHUB_APP_TOKEN || github.token }}
with:
agent: ${{ github.workspace }}/.github/agents/whats-new.yaml
prompt: >-
Use the curate-whats-new skill to review documentation pull requests merged from
${{ steps.period.outputs.start }} through
${{ steps.period.outputs.end }}, inclusive.
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
github-token: ${{ env.GITHUB_APP_TOKEN || github.token }}
timeout: 1200
- name: Verify agent changes
env:
PERIOD_START: ${{ steps.period.outputs.start }}
PERIOD_END: ${{ steps.period.outputs.end }}
run: |
CHANGED=$(git status --short | awk '$2 != "data/whats-new.json" && $2 != ".pr-body.md" { print }')
if [ -n "$CHANGED" ]; then
echo "::error::Agent changed files outside its allowed scope"
echo "$CHANGED"
exit 1
fi
test -f .pr-body.md
npx prettier --check data/whats-new.json
node hack/validate-whats-new.mjs data/whats-new.json "$PERIOD_START" "$PERIOD_END"
- name: Show proposal
if: inputs.dry-run == true
run: |
git diff -- data/whats-new.json
cat .pr-body.md
- name: Detect changes
id: changes
if: inputs.dry-run != true
run: |
if cmp -s \
<(git show HEAD:data/whats-new.json | jq -S '.items') \
<(jq -S '.items' data/whats-new.json); then
echo "changed=false" >> "$GITHUB_OUTPUT"
else
echo "changed=true" >> "$GITHUB_OUTPUT"
fi
- name: Close obsolete pull request
if: steps.changes.outputs.changed == 'false'
env:
GH_TOKEN: ${{ env.GITHUB_APP_TOKEN || github.token }}
run: |
EXISTING_PR=$(gh pr list --state open --head "$BRANCH_NAME" --json url --jq '.[0].url // empty')
if [ -n "$EXISTING_PR" ]; then
gh pr close "$EXISTING_PR" \
--comment "The latest sliding-window review no longer proposes changes to the published highlights."
fi
- name: Commit changes
if: steps.changes.outputs.changed == 'true'
env:
PERIOD_END: ${{ steps.period.outputs.end }}
run: |
git checkout -B "$BRANCH_NAME"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add data/whats-new.json
git commit -m "docs: update what's new for $PERIOD_END"
- name: Create or update pull request
if: steps.changes.outputs.changed == 'true'
env:
GH_TOKEN: ${{ env.GITHUB_APP_TOKEN || github.token }}
run: |
gh auth setup-git
EXISTING_PR=$(gh pr list --state open --head "$BRANCH_NAME" --json url --jq '.[0].url // empty')
if [ -n "$EXISTING_PR" ]; then
git push --force --set-upstream origin "$BRANCH_NAME"
gh pr edit "$EXISTING_PR" \
--title "docs: update what's new" \
--body-file .pr-body.md
else
git push --force --set-upstream origin "$BRANCH_NAME"
gh pr create \
--title "docs: update what's new" \
--body-file .pr-body.md \
--base main \
--head "$BRANCH_NAME"
fi
+1 -8
View File
@@ -16,14 +16,7 @@ reference material for everyday development and operations tasks.
- [Manuals](/manuals/): Install, configure, and use Docker products.
- [Reference](/reference/): Browse CLI, API, and file format documentation.
## Featured topics
- [Docker Hardened Images](/dhi/)
- [Get started with Docker Sandboxes](/ai/sandboxes/get-started/)
- [Docker Desktop overview](/desktop/)
- [Install Docker Engine](/engine/install/)
- [Dockerfile reference](/reference/dockerfile/)
- [Docker Build overview](/build/)
{{< whats-new >}}
## Common questions
+96
View File
@@ -0,0 +1,96 @@
{
"period_start": "2026-07-13",
"period_end": "2026-08-11",
"items": [
{
"product": "Docker Desktop",
"title": "Use Docker VMM on Mac and Windows",
"description": "Run Docker Desktop with Docker's container-optimized hypervisor on supported Mac and Windows systems.",
"url": "/desktop/features/vmm/",
"published": "2026-08-10",
"source_prs": [25702, 25726],
"featured": true
},
{
"product": "Docker Sandboxes",
"title": "Connect sandboxes through an MCP gateway",
"description": "Register local or remote MCP servers on the host, reuse them across sandboxes, and govern server and tool access with Cedar policies.",
"url": "/ai/sandboxes/mcp-gateway/",
"published": "2026-08-06",
"source_prs": [25559, 25707],
"featured": true
},
{
"product": "Docker Sandboxes",
"title": "Author version 2 sandbox kits",
"description": "Define kit setup, permissions, networking, and API key or OAuth credential requirements with the version 2 kit schema.",
"url": "/ai/sandboxes/customize/kit-reference/#schema-versions",
"published": "2026-08-06",
"source_prs": [25467, 25707],
"featured": false
},
{
"product": "Docker Sandboxes",
"title": "Run Claude Code with local models",
"description": "Route Claude Code sandbox requests to the bundled local-model server or an existing Ollama installation.",
"url": "/ai/sandboxes/agents/claude-code/#use-a-local-model",
"published": "2026-08-06",
"source_prs": [25641, 25707],
"featured": false
},
{
"product": "Docker AI Governance",
"title": "Search and forward audit events",
"description": "Search and export policy decisions from Docker Cloud, or forward events to Splunk, Dynatrace, and custom HTTPS endpoints.",
"url": "/ai/sandboxes/governance/audit/",
"published": "2026-08-03",
"source_prs": [25679, 25687],
"featured": true
},
{
"product": "Docker Desktop",
"title": "Install from the Microsoft Store without administrator privileges",
"description": "Fresh Microsoft Store installations on Windows use per-user mode by default.",
"url": "/desktop/release-notes/#4850",
"published": "2026-08-03",
"source_prs": [25665],
"featured": false
},
{
"product": "Docker Sandboxes",
"title": "Share agent skills across sandboxes",
"description": "Import skills from supported host agents into a persistent store that sandboxes can share.",
"url": "/ai/sandboxes/workflows/#share-agent-skills",
"published": "2026-07-24",
"source_prs": [25588],
"featured": false
},
{
"product": "Docker Sandboxes",
"title": "Connect editors and desktop apps over SSH",
"description": "Use a sandbox from VS Code, Cursor, Claude Desktop, ChatGPT, or another SSH-capable tool.",
"url": "/ai/sandboxes/integrations/",
"published": "2026-07-24",
"source_prs": [25557],
"featured": true
},
{
"product": "Docker Hardened Images",
"title": "Query the DHI catalog from AI assistants",
"description": "Search DHI repositories, inspect images, retrieve SBOMs, check CVEs, and manage mirrors through the DHI MCP server.",
"url": "/dhi/tools/mcp/",
"published": "2026-07-23",
"source_prs": [25589],
"featured": false
},
{
"product": "Docker Enterprise",
"title": "Authenticate GitHub Actions with OIDC connections",
"description": "Exchange GitHub OIDC tokens for short-lived access to Docker resources without storing long-lived workflow credentials.",
"url": "/enterprise/security/oidc-connections/",
"published": "2026-07-21",
"source_prs": [25336],
"featured": true
}
]
}
+92
View File
@@ -0,0 +1,92 @@
#!/usr/bin/env node
import fs from "node:fs";
const [path, expectedStart, expectedEnd] = process.argv.slice(2);
if (!expectedEnd) {
throw new Error(`usage: ${process.argv[1]} FILE START END`);
}
const data = JSON.parse(fs.readFileSync(path, "utf8"));
const requiredItemKeys = [
"product",
"title",
"description",
"url",
"published",
"source_prs",
"featured",
];
if (data.period_start !== expectedStart) {
throw new Error(`period_start must be ${expectedStart}`);
}
if (data.period_end !== expectedEnd) {
throw new Error(`period_end must be ${expectedEnd}`);
}
if (!Array.isArray(data.items)) {
throw new Error("items must be an array");
}
for (const [index, item] of data.items.entries()) {
const missing = requiredItemKeys.filter((key) => !(key in item));
if (missing.length > 0) {
throw new Error(`item ${index + 1} is missing: ${missing.join(", ")}`);
}
for (const key of requiredItemKeys.slice(0, 4)) {
if (typeof item[key] !== "string" || item[key].trim() === "") {
throw new Error(`item ${index + 1} has an empty ${key}`);
}
}
if (!item.url.startsWith("/") || item.url.startsWith("/manuals/")) {
throw new Error(`item ${index + 1} url must be an internal published URL`);
}
if (
typeof item.published !== "string" ||
!/^\d{4}-\d{2}-\d{2}$/.test(item.published) ||
new Date(`${item.published}T00:00:00Z`).toISOString().slice(0, 10) !==
item.published
) {
throw new Error(`item ${index + 1} has an invalid published date`);
}
if (item.published < data.period_start || item.published > data.period_end) {
throw new Error(`item ${index + 1} published date is outside the period`);
}
if (
!Array.isArray(item.source_prs) ||
item.source_prs.length === 0 ||
!item.source_prs.every((number) => Number.isInteger(number) && number > 0)
) {
throw new Error(
`item ${index + 1} source_prs must contain pull request numbers`,
);
}
if (typeof item.featured !== "boolean") {
throw new Error(`item ${index + 1} featured must be a boolean`);
}
}
const featuredCount = data.items.filter((item) => item.featured).length;
const expectedFeaturedCount = Math.min(5, data.items.length);
if (featuredCount !== expectedFeaturedCount) {
throw new Error(
`items must contain ${expectedFeaturedCount} featured highlights`,
);
}
const publishedDates = data.items.map((item) => item.published);
const sortedDates = [...publishedDates].sort().reverse();
if (publishedDates.join() !== sortedDates.join()) {
throw new Error("items must be sorted by published date, newest first");
}
const titles = data.items.map((item) => item.title);
if (new Set(titles).size !== titles.length) {
throw new Error("items must not contain duplicate titles");
}
const urls = data.items.map((item) => item.url);
if (new Set(urls).size !== urls.length) {
throw new Error("items must not contain duplicate URLs");
}
+79
View File
@@ -0,0 +1,79 @@
{{- $data := index hugo.Data "whats-new" -}}
{{- with $data.items -}}
{{- $items := sort . "published" "desc" -}}
{{- $more := where $items "featured" false -}}
<section class="py-10 md:py-12" x-data="{ expanded: false }">
<div class="container mx-auto max-w-4xl px-4">
<header class="mb-6 border-b border-gray-200 pb-4 dark:border-gray-800">
<h2 class="text-2xl font-semibold text-gray-900 dark:text-white">
What's new
</h2>
</header>
<ol>
{{ range $items }}
<li
{{ if not .featured }}
x-cloak x-show="expanded" x-collapse.duration.300ms
{{ end }}
class="grid grid-cols-[1fr] md:grid-cols-[5rem_1fr] md:gap-x-6"
>
<time
class="hidden pt-5 text-right text-sm text-gray-500 md:block dark:text-gray-400"
datetime="{{ .published }}"
>{{ .published | time.Format "Jan 2" }}</time
>
<a
href="{{ .url }}"
class="group relative block border-l border-gray-200 py-5 pl-7 dark:border-gray-700"
>
<span
aria-hidden="true"
class="absolute top-6 -left-[5px] size-[9px] rounded-full bg-gray-300 ring-4 ring-gray-50 transition group-hover:bg-blue-500 dark:bg-gray-600 dark:ring-gray-950 dark:group-hover:bg-blue-400"
></span>
<span
class="mb-1 flex flex-wrap items-center gap-x-2 text-sm font-medium text-blue-600 dark:text-blue-400"
>
{{ .product }}
<time
class="font-normal text-gray-500 md:hidden dark:text-gray-400"
datetime="{{ .published }}"
>{{ .published | time.Format "Jan 2" }}</time
>
</span>
<span class="flex items-center gap-2">
<span
class="font-medium text-gray-900 transition group-hover:text-blue-500 dark:text-white dark:group-hover:text-blue-400"
>{{ .title }}</span
>
<span
class="icon-svg shrink-0 text-gray-400 transition group-hover:translate-x-1 group-hover:text-blue-500 dark:text-gray-500 dark:group-hover:text-blue-400"
>
{{ partialCached "icon" "arrow-right" "arrow-right" }}
</span>
</span>
<span class="mt-1 block text-sm text-gray-600 dark:text-gray-300"
>{{ .description }}</span
>
</a>
</li>
{{ end }}
</ol>
{{ with $more }}
<button
type="button"
@click="expanded = !expanded"
:aria-expanded="expanded"
class="mt-4 ml-7 inline-flex items-center gap-1 text-sm font-medium text-blue-600 hover:text-blue-500 md:ml-[6.5rem] dark:text-blue-400 dark:hover:text-blue-300"
>
<span x-text="expanded ? 'Show less' : 'Show more'">Show more</span>
<span class="icon-svg icon-sm" x-show="!expanded">
{{ partialCached "icon" "chevron-down" "chevron-down" }}
</span>
<span class="icon-svg icon-sm" x-cloak x-show="expanded">
{{ partialCached "icon" "chevron-up" "chevron-up" }}
</span>
</button>
{{ end }}
</div>
</section>
{{- end -}}
@@ -0,0 +1,9 @@
{{- $data := index hugo.Data "whats-new" -}}
{{- with $data.items }}
## What's new
{{ range . }}
{{ printf "- [%s: %s](%s): %s (%s)\n" .product .title .url .description (.published | time.Format "Jan 2, 2006") -}}
{{ end }}
{{- end }}
+1 -37
View File
@@ -116,43 +116,7 @@
</div>
</section>
<!-- Popular Topics Section -->
<section class="py-8">
<div class="container mx-auto max-w-3xl px-4">
<h2
class="mb-8 text-center text-2xl font-semibold text-gray-900 dark:text-white"
>
Featured topics
</h2>
<div
class="divide-y divide-gray-200 rounded-xl border border-gray-200 bg-white dark:divide-gray-700 dark:border-gray-700 dark:bg-gray-900"
>
{{ range slice
(dict "url" "/dhi/" "title" "Docker Hardened Images")
(dict "url" "/ai/sandboxes/get-started/" "title" "Get started with Docker Sandboxes")
(dict "url" "/desktop/" "title" "Docker Desktop overview")
(dict "url" "/engine/install/" "title" "Install Docker Engine")
(dict "url" "/reference/dockerfile/" "title" "Dockerfile reference")
(dict "url" "/build/" "title" "Docker Build overview")
}}
<a
href="{{ .url }}"
class="group flex items-center justify-between px-4 py-3 transition hover:bg-blue-50 dark:hover:bg-gray-700"
>
<span
class="text-gray-900 transition group-hover:text-blue-500 dark:text-white dark:group-hover:text-blue-400"
>{{ .title }}</span
>
<span
class="icon-svg text-gray-400 transition group-hover:translate-x-1 dark:text-gray-500"
>
{{ partialCached "icon" "arrow-right" "arrow-right" }}
</span>
</a>
{{ end }}
</div>
</div>
</section>
{{ partial "whats-new.html" . }}
</main>
<footer>{{ partialCached "footer.html" . }}</footer>
</body>