Fix homepage install controls and split docs install sections

Move inline script inside Layout for proper View Transitions support,
redesign install pills as connected tabs above the command bar, and
split the combined pnpm/bun docs section into separate headings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Advait Paliwal
2026-03-24 20:50:33 -07:00
parent 563068180f
commit 5fab329ad1
2 changed files with 91 additions and 76 deletions

View File

@@ -25,23 +25,33 @@ irm https://feynman.is/install.ps1 | iex
This installs the Windows runtime bundle under `%LOCALAPPDATA%\Programs\feynman`, adds its launcher to your user `PATH`, and lets you re-run the installer at any time to update.
## pnpm / bun
## pnpm
If you already have Node.js 20.18.1+ installed, you can install Feynman globally via `pnpm` or `bun`:
If you already have Node.js 20.18.1+ installed, you can install Feynman globally via `pnpm`:
```bash
pnpm add -g @companion-ai/feynman
bun add -g @companion-ai/feynman
```
Or run it directly without installing:
```bash
pnpm dlx @companion-ai/feynman
```
## bun
```bash
bun add -g @companion-ai/feynman
```
Or run it directly without installing:
```bash
bunx @companion-ai/feynman
```
The package-manager distribution ships the same core application but depends on Node.js being present on your system. The standalone installer is preferred because it bundles its own Node runtime and works without a separate Node installation.
Both package-manager distributions ship the same core application but depend on Node.js being present on your system. The standalone installer is preferred because it bundles its own Node runtime and works without a separate Node installation.
## Post-install setup

View File

@@ -57,14 +57,17 @@ const installCommands = [
</div>
<div class="flex w-full max-w-3xl flex-col items-center gap-4">
<div class="flex flex-wrap items-center justify-center gap-2">
{installCommands.map((entry) => (
<div class="flex w-full flex-col">
<div class="flex self-start">
{installCommands.map((entry, i) => (
<button
class:list={[
"install-toggle rounded-full border px-3 py-1.5 text-sm font-medium transition-colors",
"install-toggle px-4 py-2 text-sm font-medium transition-colors cursor-pointer",
i === 0 ? "rounded-tl-lg" : "",
i === installCommands.length - 1 ? "rounded-tr-lg" : "",
entry.label === installCommands[0].label
? "border-foreground bg-foreground text-background"
: "border-border bg-background text-muted-foreground hover:text-foreground",
? "bg-muted text-foreground"
: "bg-muted/30 text-muted-foreground hover:text-foreground hover:bg-muted/50",
]}
data-command={entry.command}
aria-label={`Show ${entry.label} install command`}
@@ -73,10 +76,9 @@ const installCommands = [
</button>
))}
</div>
<button
id="install-cmd"
class="group flex w-full items-center justify-between gap-3 rounded-lg bg-muted px-4 py-3 text-left font-mono text-sm transition-colors hover:bg-muted/80 cursor-pointer"
class="group flex w-full items-center justify-between gap-3 rounded-b-lg rounded-tr-lg bg-muted px-4 py-3 text-left font-mono text-sm transition-colors hover:bg-muted/80 cursor-pointer"
data-command={installCommands[0].command}
aria-label="Copy install command"
>
@@ -84,6 +86,7 @@ const installCommands = [
<svg id="install-copy" class="size-4 shrink-0 text-muted-foreground transition-colors group-hover:text-foreground" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
<svg id="install-check" class="hidden size-4 shrink-0 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path d="M20 6L9 17l-5-5"/></svg>
</button>
</div>
<div class="flex items-center gap-3">
<a href="/docs/getting-started/installation">
@@ -214,50 +217,52 @@ const installCommands = [
</section>
</div>
</Layout>
<script is:inline>
function initCopyBtn() {
(function () {
function init() {
var toggles = Array.from(document.querySelectorAll(".install-toggle"))
var commandBtn = document.getElementById("install-cmd")
var commandText = document.getElementById("install-command")
var copyLabel = document.getElementById("install-copy")
var checkLabel = document.getElementById("install-check")
var btn = document.getElementById("install-cmd")
var text = document.getElementById("install-command")
var copyIcon = document.getElementById("install-copy")
var checkIcon = document.getElementById("install-check")
if (!btn || !text || !copyIcon || !checkIcon) return
toggles.forEach(function (toggle) {
if (toggle._toggleBound) return
toggle._toggleBound = true
if (toggle._b) return
toggle._b = true
toggle.addEventListener("click", function () {
var command = toggle.getAttribute("data-command")
if (!commandBtn || !commandText || !command) return
commandBtn.setAttribute("data-command", command)
commandText.textContent = command
toggles.forEach(function (item) {
item.classList.remove("border-foreground", "bg-foreground", "text-background")
item.classList.add("border-border", "bg-background", "text-muted-foreground")
var cmd = toggle.getAttribute("data-command")
if (!cmd) return
btn.setAttribute("data-command", cmd)
text.textContent = cmd
toggles.forEach(function (t) {
t.classList.remove("bg-muted", "text-foreground")
t.classList.add("bg-muted/30", "text-muted-foreground", "hover:text-foreground", "hover:bg-muted/50")
})
toggle.classList.remove("border-border", "bg-background", "text-muted-foreground")
toggle.classList.add("border-foreground", "bg-foreground", "text-background")
toggle.classList.remove("bg-muted/30", "text-muted-foreground", "hover:text-foreground", "hover:bg-muted/50")
toggle.classList.add("bg-muted", "text-foreground")
})
})
if (commandBtn && !commandBtn._copyBound) {
commandBtn._copyBound = true
commandBtn.addEventListener("click", function () {
var command = commandBtn.getAttribute("data-command")
if (!command) return
navigator.clipboard.writeText(command).then(function () {
if (!copyLabel || !checkLabel) return
copyLabel.classList.add("hidden")
checkLabel.classList.remove("hidden")
if (!btn._b) {
btn._b = true
btn.addEventListener("click", function () {
var cmd = btn.getAttribute("data-command")
if (!cmd) return
navigator.clipboard.writeText(cmd).then(function () {
copyIcon.classList.add("hidden")
checkIcon.classList.remove("hidden")
setTimeout(function () {
copyLabel.classList.remove("hidden")
checkLabel.classList.add("hidden")
copyIcon.classList.remove("hidden")
checkIcon.classList.add("hidden")
}, 2000)
})
})
})
}
initCopyBtn()
document.addEventListener("astro:after-swap", initCopyBtn)
}
init()
document.addEventListener("astro:after-swap", init)
})()
</script>
</Layout>