## Description
Gordon answers everything in plain markdown, even when the answer is a
procedure or a comparison. Those answers are harder to scan than they
need to be. This PR lets Gordon put a small card inside its answer when
that helps:
- **Steps** for procedures, with commands in code boxes
- **Tables** for comparisons (limits, plans, options)
- **Notes** for warnings, active incidents or tips
- **Code snippets** for config files
- **Follow-up questions** as chips. Selecting one asks it as your next
question.
```
You can containerize it with docker init, then run it with Compose.
┌───────────────────────────────────────────────┐
│ Containerize a Node.js app │
│ ① Generate the Docker assets │
│ [ docker init ] │
│ ② Build and run the app │
│ [ docker compose up --build ] │
│ ▌ Use Docker Hardened Images for security │
│ ( How do I add a database service? ) │
└───────────────────────────────────────────────┘
Sources:
- https://docs.docker.com/guides/nodejs/
```
Short answers, explanations and greetings stay plain markdown.
### How it works
```
chat ── "I can draw cards" (ui: "openui-v1") ──▶ Gordon
chat ◀── markdown answer, with at most one ```openui-lang block ── Gordon
chat: draws the block as a card, everything else as markdown
```
- The chat sends one extra field with each question. Only then does
Gordon's backend tell the model it may add a card. Without the field,
answers are the same as today.
- A card is a fenced block of [OpenUI
Lang](https://www.openui.com/docs/openui-lang) inside the normal answer
text, so the conversation history and stored messages stay plain text.
- The chat draws the block with its own styles. Anything it doesn't
recognize is left out, and if the card code can't load, the block shows
as a plain code block.
### Visual change to all answers
Gordon's answers are no longer inside a grey bubble. A framed card
inside the grey bubble looked like a box in a box. Answers now sit on
the panel background at full width, and the card stands out as its own
block. Your own messages keep the blue bubble.
<!-- screenshots: before / after, light and dark -->
### Rollout
- Cards are **on in local builds and deploy previews**, and **off in
production**.
- To turn them on in production, set `params.gordon.cards: true` in
`hugo.yaml`.
- Gordon's backend already supports this, so no other change is needed.
### Other notes
- **Safety:** card text from the model is only ever inserted as text,
never as HTML.
- **Page weight:** the card code (about 120 KB gzipped) is a separate
bundle. It's only downloaded when an answer first contains a card, never
on page load.
- **Copy button:** copies the answer with cards written out as markdown
(numbered steps, a table, `> [!NOTE]`), not the raw card code. Follow-up
chips are left out.
- **Instructions Gordon gets:** the prompt lives entirely in Gordon's
backend, which injects it when the chat sends `ui: "openui-v1"`. This
repo only has the component list the chat draws
(`assets/js/gordon-cards/components.mjs`). The backend's card prompt
describes the same components, so when adding or changing one, the
backend's prompt changes with it.
- **New dependencies:** `@openuidev/lang-core` (the OpenUI Lang parser)
and `zod`, pinned. Its usage telemetry never runs in a browser, and
`.npmrc` already blocks install scripts.
### How to test
1. Run `docker compose watch` and open http://localhost:1313. Local
builds use Gordon's staging backend.
2. Open Gordon from the header and ask:
- **Should show a card:** "How do I containerize my Node.js app?", "What
are the Docker Hub pull limits?", "How do I set up a registry mirror?"
- **Should stay plain text:** "What is Docker Scout?", "hi"
3. Select a follow-up chip. It should ask that question.
4. Select **Copy** on an answer with a card. The pasted text should be
markdown.
5. Reload the page. Cards should come back from the conversation
history.
6. Check dark mode.
7. In the browser's Network tab, check:
- each question to `/public/ask` includes `"ui": "openui-v1"`;
- `gordon-cards.js` only loads after the first answer with a card.
## Related issues or tickets
<!-- none public -->
## Reviews
- [x] Technical review
- [ ] Editorial review
- [ ] Product review
@netlify /reference/api/
[Preview the API
reference](https://deploy-preview-26048--docsdocker.netlify.app/reference/api/)
Adds a shared API reference experience for Hub, DVP, and Registry: a
catalog, operation navigation, linked schemas, request and response
examples, and matching HTML and Markdown output. A validation and
generation pipeline prepares the reference data for Hugo and runs as
part of Docker and Netlify builds.
Migrates the three specifications to OpenAPI 3.2, correcting response
shapes, nullable fields, and authentication and token examples. The Hub
migration also corrects SCIM contracts and CSV exports; DVP gains
complete report response schemas and examples. Registry keeps its
documented Hub-specific subset, with authentication and media
negotiation expressed through OpenAPI security and content fields.
Validation is strict for both checks and generation, with no exception
baseline or bypass flag. The build uses the migrated sources directly,
without migration scripts or records.
Page and specification URLs remain stable, including the DVP alias.
Operation and schema anchors change. Engine and Governance retain their
existing references.
Validated locally with Go tests, strict validation of all three
specifications (zero diagnostics), scoped formatting and lint, and a
full Hugo build checking all 176 HTML/Markdown page pairs, example
parity, reference links, and byte-identical published specifications. A
browser check confirmed raw CSV rendering and the generated request
header. No live API requests were made.
Bumps @docker/marlin-sdk-web-public from 0.5.0 to 0.6.0.
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Adds a Hugo codeblock render hook that turns ```mermaid fences into
<pre class="mermaid"> containers, plus a lazily-loaded mermaid bundle
that only ships on pages that actually contain a diagram. Mirrors the
existing YouTube embed pattern (Store flag + conditional script tag in
baseof) so the main scripts.js bundle stays unchanged.
Introduces @docker/marlin-sdk-web-public for first-party pageview and
click analytics, bundled into scripts.js via Hugo's existing js.Build
pipeline. Config is emitted to window.__marlinConfig from head.html and
gated to prod/staging only.
Renames data-heap-id attributes on the markdown-dropdown buttons to
marlin-action so they are picked up by the SDK's auto-click tracking.
TODO: replace the REPLACE-ME endpoint placeholders in hugo.yaml with
the canonical Marlin ingestion URLs from the data-platform team.
TODO: heap.track() calls in youtube-script.html are left in place —
the public SDK exposes no equivalent track() method, so video
play/pause events cannot be migrated yet.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
Replaces @material-symbols/svg-400 (5.5 MB) with heroicons (488 KB) — an
11x reduction in icon asset size. Heroicons uses a single consistent
distribution format (24px solid SVGs, fill="currentColor") eliminating
the need for the separate utils/svg.html partial, the icon-svg-stroke CSS
utility, and the dual-path resolution logic in icon.html.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
All mutable action tags replaced with verified commit SHAs to prevent
supply-chain attacks via tag mutation. package.json ^ ranges replaced
with exact versions from package-lock.json.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Extract classes to utilities and components.
- Reduce number of colors used.
- Harmonize button colors.
- Restyle admonitions.
- Move **Page options** button to main article.
- Various color tweaks.
Separate dev dependencies like prettier to prevent them from being
installed to build the site, unnecessarily.
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
This commit adds support for icon optimization using inline svgs,
improving site performance.
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>