diff --git a/apps/marketing/astro.config.mjs b/apps/marketing/astro.config.mjs index 877af5b812..30275f77a4 100644 --- a/apps/marketing/astro.config.mjs +++ b/apps/marketing/astro.config.mjs @@ -5,6 +5,7 @@ import { unstable_readConfig } from "wrangler"; import tailwindcss from "@tailwindcss/vite"; import react from "@astrojs/react"; +import mdx from "@astrojs/mdx"; import cloudflare from "@astrojs/cloudflare"; @@ -29,7 +30,7 @@ const wranglerPublicDefine = () => { export default defineConfig({ site: "https://executor.sh", output: "server", - integrations: [react()], + integrations: [react(), mdx()], vite: { plugins: [tailwindcss()], define: wranglerPublicDefine(), diff --git a/apps/marketing/package.json b/apps/marketing/package.json index 6b3592652e..44365e82f4 100644 --- a/apps/marketing/package.json +++ b/apps/marketing/package.json @@ -15,10 +15,11 @@ }, "dependencies": { "@astrojs/cloudflare": "^13.0.0", + "@astrojs/mdx": "^6.0.3", "@astrojs/react": "^5.0.4", "@executor-js/react": "workspace:*", "@tailwindcss/vite": "^4.2.2", - "astro": "^6.1.3", + "astro": "^6.4.8", "clsx": "^2.1.1", "motion": "^12.38.0", "posthog-js": "^1.372.5", diff --git a/apps/marketing/public/llms.txt b/apps/marketing/public/llms.txt index d98199e407..754a10eaea 100644 --- a/apps/marketing/public/llms.txt +++ b/apps/marketing/public/llms.txt @@ -23,6 +23,11 @@ Run it your way: local CLI, a desktop app, hosted Executor Cloud, or self-hosted - [Pricing](https://executor.sh/#pricing): plans for Executor Cloud. - [Install](https://executor.sh/#install): install the CLI and connect your first agent. +## Blog + +- [MCPs, CLIs, and APIs are the same thing](https://executor.sh/blog/mcp-cli-same-thing): interactive tour of tool-calling envelopes, why context bloat is a client problem, and why the MCP-vs-CLI debate is a category error. +- [Why MCP had so many growing pains](https://executor.sh/blog/why-mcp-had-growing-pains): what went wrong in MCP's first year and where it goes from here. + ## Source - [GitHub](https://github.com/UsefulSoftwareCo/executor): source for the integration layer, plugins, and hosts. diff --git a/apps/marketing/src/components/blog/context-flood-demo.tsx b/apps/marketing/src/components/blog/context-flood-demo.tsx new file mode 100644 index 0000000000..884fd057bc --- /dev/null +++ b/apps/marketing/src/components/blog/context-flood-demo.tsx @@ -0,0 +1,330 @@ +"use client"; + +/* eslint-disable react/forbid-elements -- blog widgets use bespoke styled + controls; the product design-system + ))} + + + ); +} + +export function ContextFloodDemo() { + const [iface, setIface] = useState("mcp"); + const [loading, setLoading] = useState("eager"); + + const toolTok = CELL_TOK[iface][loading]; + const total = cellTotal(iface, loading); + const totalDisplay = useAnimatedNumber(total); + const flooding = loading === "eager"; + const Body = CELL_BODY[iface][loading]; + + return ( +
+

+ {cellName(iface, loading)}: about {fmt(total)} tokens spent before your first message. +

+ +
+ setIface(v as Interface_)} + options={[ + { id: "cli", label: "CLI" }, + { id: "mcp", label: "MCP server" }, + ]} + /> + setLoading(v as Loading)} + options={[ + { id: "lazy", label: "On demand" }, + { id: "eager", label: "Everything up front" }, + ]} + /> +
+ +
+
+
+ + + + + + {cellName(iface, loading)} · what the model sees +
+
+            
+              
+                {"// system prompt — ~"}
+                {fmt(SYSTEM_TOK)}
+                {" tok\n"}
+                {"You are a helpful assistant. Rules, tone, safety —\n"}
+                {"the fixed part of every request.\n\n"}
+                {"// tool definitions — ~"}
+                {fmt(toolTok)}
+                {" tok\n"}
+              
+              
+            
+          
+
+
+
~{fmt(totalDisplay)}
+
tokens before your first message
+
+ resent with every request +
+
+
+ +
+ {(["cli", "mcp"] as const).flatMap((i) => + (["lazy", "eager"] as const).map((l) => ( + + )), + )} +
+ +
+ Flip the interface: the number barely moves. Flip the loading strategy: ~20x. The bloat + lives on one axis, and it is not the protocol axis. +
+
+ ); +} diff --git a/apps/marketing/src/components/blog/install-race-demo.tsx b/apps/marketing/src/components/blog/install-race-demo.tsx new file mode 100644 index 0000000000..a33828ca78 --- /dev/null +++ b/apps/marketing/src/components/blog/install-race-demo.tsx @@ -0,0 +1,134 @@ +"use client"; + +/* eslint-disable react/forbid-elements -- blog widgets use bespoke styled + controls; the product design-system + + +
+ {LANES.map((lane) => { + const shown = visibleSteps(lane); + const laneDone = shown >= lane.steps.length && tick >= 0; + return ( +
+
+ {lane.title} + {lane.sub} +
+
+ {lane.steps.map((s, idx) => ( +
+
+ ))} +
+
+ {laneDone + ? `${lane.steps.length} steps · ${lane.steps.some((s) => s.bad) ? "1 restart, session lost" : "no restart"}` + : " "} +
+
+ ); + })} +
+ +
+ The restart is not in the protocol. MCP lets a client connect to a new server whenever it + wants and notifications/tools/list_changed exists so + the tool list can update in place. The middle lane is a client implementation choice. +
+ + ); +} diff --git a/apps/marketing/src/components/blog/mcp-to-cli-demo.tsx b/apps/marketing/src/components/blog/mcp-to-cli-demo.tsx new file mode 100644 index 0000000000..c85ffb9d59 --- /dev/null +++ b/apps/marketing/src/components/blog/mcp-to-cli-demo.tsx @@ -0,0 +1,155 @@ +"use client"; + +/* eslint-disable react/forbid-elements -- blog widgets use bespoke styled + controls; the product design-system + + +
+
+
+ MCP server github · 44 tools +
+
+ {TOOLS.map((t) => ( +
+ {t.slug} + {t.help} + {t.destructive ? destructive : null} +
+ ))} +
… {REST} more
+
+
+ + + +
+
+ Generated CLI{" "} + {done ? "github · 44 commands" : "—"} +
+
+
+ + + + + +
+
+              
+                {"$ "}
+                {"github --help\n"}
+                {compiled ? (
+                  <>
+                    {"Commands:\n"}
+                    {TOOLS.slice(0, Math.min(built, TOOLS.length)).map((t) => (
+                      
+                        {"  "}
+                        {t.cli.padEnd(17)}
+                        {t.help}
+                        {t.destructive ?  [destructive] : null}
+                        {"\n"}
+                      
+                    ))}
+                    {built >= totalRows ? (
+                      {`  … ${REST} more\n`}
+                    ) : null}
+                  
+                ) : (
+                  {"\n(nothing here yet — press Compile to CLI)"}
+                )}
+              
+            
+
+
+
+ +
+ {done + ? "44 tools in, 44 commands out. Names, schemas, descriptions, and the destructive flag all survived. The only thing that changed is how you invoke them." + : " "} +
+ + ); +} diff --git a/apps/marketing/src/components/blog/same-call-demo.tsx b/apps/marketing/src/components/blog/same-call-demo.tsx new file mode 100644 index 0000000000..104f1fd085 --- /dev/null +++ b/apps/marketing/src/components/blog/same-call-demo.tsx @@ -0,0 +1,173 @@ +"use client"; + +/* eslint-disable react/forbid-elements -- blog widgets use bespoke styled + controls (segmented toggles, mono labels); the product design-system + + ))} + + +
+
+ + + + + + create an issue on acme/api +
+
+          
+            
+          
+        
+
+
+ The highlighted parts are the operation. Everything else is + the envelope. +
+ + ); +} diff --git a/apps/marketing/src/components/blog/shared.ts b/apps/marketing/src/components/blog/shared.ts new file mode 100644 index 0000000000..84e38547c2 --- /dev/null +++ b/apps/marketing/src/components/blog/shared.ts @@ -0,0 +1,72 @@ +import { useEffect, useRef, useState } from "react"; + +/** Formats 12345 as "12,345". */ +export const fmt = (n: number) => n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); + +export function usePrefersReducedMotion(): boolean { + const [reduced, setReduced] = useState(false); + useEffect(() => { + const mq = window.matchMedia("(prefers-reduced-motion: reduce)"); + const sync = () => setReduced(mq.matches); + sync(); + mq.addEventListener("change", sync); + return () => mq.removeEventListener("change", sync); + }, []); + return reduced; +} + +/** Eases a displayed integer toward `target` with requestAnimationFrame. */ +export function useAnimatedNumber(target: number): number { + const reduced = usePrefersReducedMotion(); + const [display, setDisplay] = useState(target); + const fromRef = useRef(target); + const rafRef = useRef(null); + + useEffect(() => { + if (reduced) { + setDisplay(target); + fromRef.current = target; + return; + } + const from = fromRef.current; + if (from === target) return; + const start = performance.now(); + const dur = 450; + const tick = (now: number) => { + const t = Math.min(1, (now - start) / dur); + const eased = 1 - Math.pow(1 - t, 3); + setDisplay(Math.round(from + (target - from) * eased)); + if (t < 1) { + rafRef.current = requestAnimationFrame(tick); + } else { + fromRef.current = target; + } + }; + rafRef.current = requestAnimationFrame(tick); + return () => { + if (rafRef.current) cancelAnimationFrame(rafRef.current); + fromRef.current = target; + }; + }, [target, reduced]); + + return display; +} + +/** True while `ref`'s element is at least partially on screen. Autoplaying + * widgets gate their timers on this so nothing animates offscreen. */ +export function useInView(ref: React.RefObject): boolean { + const [inView, setInView] = useState(false); + useEffect(() => { + const el = ref.current; + if (!el || typeof IntersectionObserver === "undefined") { + setInView(true); + return; + } + const io = new IntersectionObserver(([entry]) => setInView(entry?.isIntersecting ?? false), { + threshold: 0.2, + }); + io.observe(el); + return () => io.disconnect(); + }, [ref]); + return inView; +} diff --git a/apps/marketing/src/content.config.ts b/apps/marketing/src/content.config.ts index 11e6607ac2..d50e478c65 100644 --- a/apps/marketing/src/content.config.ts +++ b/apps/marketing/src/content.config.ts @@ -1,11 +1,12 @@ import { defineCollection, z } from "astro:content"; import { glob } from "astro/loaders"; -// Blog posts authored as Markdown under src/content/blog. The glob loader -// turns each file into a collection entry whose `id` is the slug used in the -// /blog/[...slug] route (filename without extension). +// Blog posts authored as Markdown or MDX under src/content/blog. The glob +// loader turns each file into a collection entry whose `id` is the slug used +// in the /blog/[...slug] route (filename without extension). MDX is for posts +// that embed interactive island components. const blog = defineCollection({ - loader: glob({ pattern: "**/*.md", base: "./src/content/blog" }), + loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/content/blog" }), schema: z.object({ title: z.string(), description: z.string(), diff --git a/apps/marketing/src/content/blog/mcp-cli-same-thing.mdx b/apps/marketing/src/content/blog/mcp-cli-same-thing.mdx new file mode 100644 index 0000000000..efc6d1e044 --- /dev/null +++ b/apps/marketing/src/content/blog/mcp-cli-same-thing.mdx @@ -0,0 +1,81 @@ +--- +title: "MCPs, CLIs, and APIs are the same thing" +description: "You can compile an MCP server into a CLI without changing a single tool. An interactive look at what tool calling actually is, why context bloat is a client problem, and why the MCP-vs-CLI debate is a category error." +date: 2026-08-23 +author: "Rhys Sullivan" +--- + +import { SameCallDemo } from "../../components/blog/same-call-demo"; +import { McpToCliDemo } from "../../components/blog/mcp-to-cli-demo"; +import { ContextFloodDemo } from "../../components/blog/context-flood-demo"; +import { InstallRaceDemo } from "../../components/blog/install-race-demo"; + +Every few weeks the debate comes back around: CLIs beat MCP, MCP is dead, agents just need bash. Benchmarks get run, threads get written, and everyone sorts into camps. + +The whole argument is built on a category error. MCP servers, CLIs, and APIs are not three competing technologies. They are three envelopes around the same thing: an agent invoking a tool. + +Here is one operation — create a GitHub issue — in all three envelopes. Flip between them and watch what actually changes. + + + +Same operation, same arguments, same GitHub on the other end. What changes is the delivery mechanism: a JSON-RPC message from a connected client, a process spawned in a shell, an HTTP request with a bearer token. Every take of the form "CLIs beat MCP" is a claim about envelopes, not about capabilities. + +And because they are just envelopes, you can convert between them freely. Let's do that. + +## Compile an MCP server into a CLI + +The official GitHub MCP server publishes 44 tools. Each one has a name, a JSON schema for its arguments, a description, and annotations like whether it is destructive. Press `Compile to CLI`. + + + +Nothing about the tools changed. Not the count, not the schemas, not the descriptions. What changed is the execution environment you need to invoke them: on the left, a client that speaks JSON-RPC; on the right, a shell, a filesystem, a `PATH`, and a process per invocation. + +Note the direction of that arrow, though. MCP to CLI is mechanical, because the catalog declares its entire action space up front. The reverse is not. A CLI declares nothing — the only way to find out what `gh` can do is to recursively run `--help` and hope the help text is complete. Keep that asymmetry in mind; it comes back later. + +If context bloat were a property of MCP, this compile step would be a miracle cure: same 44 tools, bloat gone. That is obviously absurd, which tells you the bloat was never coming from the protocol. + +## Context bloat is a loading strategy + +When someone says "MCP floods your context window," the thing being described is a client that takes every tool schema from every connected server and pastes all of them into every request. That is a decision the client made. The protocol did not make it. + +There are two independent choices hiding in every setup: which interface you use, and how tools get loaded into context. Try both axes and watch which one the number follows. + + + +The number follows the loading axis. A CLI feels cheap because no one would ever dream of inlining the help text for every `gh` subcommand up front — the model runs `--help` for the two commands it needs, when it needs them. Then we built MCP clients that do exactly the thing nobody would do to a CLI, and blamed the protocol for the result. + +The good clients already work this way. Lazy loading, tool search, and code mode all defer tool descriptions until the model actually needs them — [Anthropic's write-up on code execution with MCP](https://www.anthropic.com/engineering/code-execution-with-mcp) describes cutting token use dramatically by having the model write code that calls tools instead of front-loading every definition. Eager transmission is a client design choice, not a requirement of the protocol. Any benchmark that measures an eager client and reports the result as a property of MCP is measuring the client. + +## The restart is not in the spec + +The strongest real advantage CLIs have today has nothing to do with tokens. It is this: + + + +Tell an agent to install a CLI and it is using it seconds later, mid-conversation. Add an MCP server to most clients and you are editing a config file, restarting the client, losing your session, and re-authenticating. That friction is real and I will not defend it. + +But locate it precisely. The protocol lets a client connect to a new server at any point in a session, and `notifications/tools/list_changed` exists so the tool list can update in place. The middle lane is a client implementation gap — the same species of gap as eager loading. Bash had 37 years of polish behind it when agents arrived; most MCP clients were built in months. That is an argument about maturity, not about which envelope is correct. + +## So when do you reach for each? + +If they are all the same thing underneath, choosing between them is about what each envelope buys you. + +**MCP** is the right envelope for interactions that are _about_ the agent session: elicitation (ask the human before doing something), MCP Apps (render UI inside the chat), triggers and notifications (push state changes back into a running session). None of that maps onto a CLI or a bare API. You also get the enumerable action space and destructive-action annotations that made the compile step above possible. + +**APIs** are right for raw data access and bulk work, and they are the fastest way to make an existing product agent-accessible. The OpenAPI spec already carries descriptions and marks what is destructive. If your MCP server is a thin mirror of your API, the spec alone was probably enough. + +**CLIs** are the best debugging experience an agent can have today, because bash composability — chain commands, grep the output, retry — is unmatched. The price is a shell and a sandbox per agent, plus an action space you cannot enumerate. Over a long enough horizon I think CLIs are for humans; today they are often the pragmatic choice. + +I wrote more about how MCP ended up with its scar tissue in [Why MCP had so many growing pains](/blog/why-mcp-had-growing-pains). + +## It's all tool calling + +You can convert an API into an MCP server, the MCP server into a CLI, and the CLI back into API calls, and at no point did the agent's actual capabilities change. What changed is the dependencies you carry — a client, a shell, an HTTP stack — and the interaction patterns you unlock. + +That is the bet behind [Executor](https://executor.sh): the harness should not care which envelope you bring. MCP server, OpenAPI spec, GraphQL, CLI — it all lands in one catalog, loaded on demand, behind one `execute` tool. My personal instance has around 10,000 tools in it, a mix of MCP servers and raw APIs, and agents work through it comfortably — not because the envelopes are exotic, but because the loading strategy is right. + +Stop arguing about envelopes. Fix the client. + +--- + +_Token figures in the loading-strategy widget are illustrative: roughly 800 tokens per tool schema, against the official GitHub MCP server's catalog of 44 tools._ diff --git a/apps/marketing/src/styles/global.css b/apps/marketing/src/styles/global.css index b23ec2755a..0f85aeb494 100644 --- a/apps/marketing/src/styles/global.css +++ b/apps/marketing/src/styles/global.css @@ -1673,3 +1673,495 @@ body { .blog-prose h3 + p { margin-top: 0; } + +/* ══ Blog interactive widgets (bw-*) ══════════════════════════════ + Shared frame + primitives for the interactive diagrams embedded in + MDX posts (src/components/blog/*). Grayscale only, per design.md: + hierarchy comes from tone, hatching, and mono labels, never hue. */ +.bw { + border: 1px solid var(--color-rule); + border-radius: 14px; + background: white; + padding: 1.1rem 1.2rem 1rem; + font-size: 13.5px; + line-height: 1.5; + color: var(--color-ink); +} +.blog-prose .bw { + margin: 2.25rem 0; +} +/* Reset the editorial pre/code treatment inside widgets so .code-window + panes keep their own chrome. */ +.blog-prose .bw pre { + margin: 0; + padding: 1.1rem 1.2rem; + background: transparent; + border: 0; + border-radius: 0; + font-size: 12.5px; + line-height: 1.7; +} +.blog-prose .bw code { + background: transparent; + border: 0; + padding: 0; + font-size: inherit; +} + +.bw-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.75rem; + flex-wrap: wrap; + margin-bottom: 0.9rem; +} +.bw-head--stack { + justify-content: flex-start; + gap: 1.5rem; +} +.bw-eyebrow { + font-family: var(--font-mono); + font-size: 11px; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--color-ink-3); +} +.bw-foot { + margin-top: 0.85rem; + padding-top: 0.75rem; + border-top: 1px solid var(--color-rule); + font-size: 12.5px; + line-height: 1.55; + color: var(--color-ink-2); + min-height: 2em; +} +.bw-mono-note { + font-family: var(--font-mono); + font-size: 11px; + color: var(--color-ink-3); + white-space: nowrap; +} +.bw-mono-inline { + font-family: var(--font-mono); + font-size: 0.92em; +} +.bw-dim { + color: var(--color-ink-3); +} +/* reset: the invariant-fact highlight. */ +.bw-hl { + background: color-mix(in srgb, var(--color-accent) 7%, transparent); + color: var(--color-ink); + font-weight: 600; + border-radius: 3px; + padding: 0 2px; +} + +/* Segmented control */ +.bw-seg { + display: inline-flex; + gap: 2px; + padding: 2px; + border: 1px solid var(--color-rule); + border-radius: 8px; + background: var(--color-surface-2); +} +.bw-seg__btn { + font-family: var(--font-mono); + font-size: 11.5px; + padding: 4px 10px; + border-radius: 6px; + border: 1px solid transparent; + color: var(--color-ink-2); + cursor: pointer; + white-space: nowrap; + transition: + color 0.15s ease, + background 0.15s ease; +} +.bw-seg__btn:hover { + color: var(--color-ink); +} +.bw-seg__btn[data-on="true"] { + background: white; + border-color: var(--color-rule); + color: var(--color-ink); + box-shadow: 0 1px 2px rgba(13, 13, 16, 0.05); +} +.bw-seg__btn:focus-visible, +.bw-btn:focus-visible, +.bw-map__cell:focus-visible { + outline: 2px solid var(--color-accent); + outline-offset: 2px; +} + +/* Secondary action button */ +.bw-btn { + font-size: 13px; + font-weight: 500; + padding: 0 13px; + height: 30px; + border: 1px solid var(--color-rule-strong); + border-radius: 8px; + background: white; + color: var(--color-ink); + cursor: pointer; + white-space: nowrap; + transition: + border-color 0.15s ease, + background 0.15s ease; +} +.bw-btn:hover { + border-color: var(--color-ink-3); + background: var(--color-surface-2); +} +.bw-btn:disabled { + opacity: 0.5; + cursor: default; +} + +/* Code-window pane inside a widget: drop the heavy marketing shadow. */ +.bw-window { + box-shadow: none; + border-radius: 10px; +} +.bw-code { + white-space: pre-wrap; + word-break: break-word; +} +.bw-code--term { + min-height: 200px; +} + +/* ── Compile pane pair (mcp-to-cli) ── */ +.bw-pair { + display: grid; + grid-template-columns: 1fr; + gap: 0.75rem; + align-items: stretch; +} +@media (min-width: 720px) { + .bw-pair { + grid-template-columns: 1fr auto 1fr; + } +} +.bw-pane { + min-width: 0; + display: flex; + flex-direction: column; + transition: opacity 0.3s ease; +} +.bw-pane[data-dim="true"] { + opacity: 0.6; +} +.bw-pane__title { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 0.5rem; + font-size: 13px; + font-weight: 600; + margin-bottom: 0.5rem; +} +.bw-pane__arrow { + align-self: center; + color: var(--color-ink-3); + font-size: 18px; + padding: 0 0.2rem; + transform: rotate(90deg); +} +@media (min-width: 720px) { + .bw-pane__arrow { + transform: none; + } +} +.bw-window--fill { + flex: 1; +} +.bw-rows { + border: 1px solid var(--color-rule); + border-radius: 10px; + overflow: hidden; + flex: 1; +} +.bw-row { + display: flex; + align-items: baseline; + gap: 0.6rem; + padding: 0.42rem 0.75rem; + border-top: 1px solid var(--color-rule); + min-width: 0; +} +.bw-row:first-child { + border-top: 0; +} +.bw-row__name { + font-family: var(--font-mono); + font-size: 12px; + color: var(--color-ink); + white-space: nowrap; +} +.bw-row__desc { + flex: 1; + min-width: 0; + font-size: 12px; + color: var(--color-ink-2); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.bw-row--rest { + font-family: var(--font-mono); + font-size: 11.5px; + color: var(--color-ink-3); +} +.bw-tag { + font-family: var(--font-mono); + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--color-ink-2); + border: 1px solid var(--color-rule-strong); + border-radius: 4px; + padding: 1px 5px; + white-space: nowrap; +} +.bw-fade-in { + animation: bw-fade 0.25s ease both; +} +@keyframes bw-fade { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +/* ── Context flood (2×2) ── */ +.bw-axis { + display: flex; + flex-direction: column; + gap: 0.35rem; +} +.bw-axis__label { + font-family: var(--font-mono); + font-size: 11px; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--color-ink-3); +} +.bw-flood { + display: grid; + grid-template-columns: 1fr; + gap: 1.25rem; + align-items: center; + margin-bottom: 1rem; +} +@media (min-width: 640px) { + .bw-flood { + grid-template-columns: 1fr 200px; + } +} +.bw-flood__win { + min-width: 0; +} +.bw-flood__scroll { + height: 300px; + overflow-y: auto; + font-size: 12px; + line-height: 1.65; +} +.bw-flood__total { + text-align: center; +} +.bw-flood__num { + font-family: var(--font-mono); + font-size: 27px; + font-weight: 600; + font-variant-numeric: tabular-nums; + letter-spacing: -0.02em; + color: var(--color-ink); +} +.bw-flood__cap { + font-size: 12px; + color: var(--color-ink-2); + margin-top: 0.15rem; +} +.bw-badge { + display: inline-block; + margin-top: 0.55rem; + font-family: var(--font-mono); + font-size: 10.5px; + text-transform: uppercase; + letter-spacing: 0.07em; + color: var(--color-ink-2); + border: 1px solid var(--color-rule-strong); + border-radius: 999px; + padding: 2px 9px; + visibility: hidden; +} +.bw-badge[data-show="true"] { + visibility: visible; +} +.bw-map { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.5rem; +} +.bw-map__cell { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 0.5rem; + padding: 0.5rem 0.7rem; + border: 1px solid var(--color-rule); + border-radius: 8px; + background: white; + cursor: pointer; + text-align: left; + transition: + border-color 0.15s ease, + background 0.15s ease; +} +.bw-map__cell:hover { + border-color: var(--color-rule-strong); +} +.bw-map__cell[data-on="true"] { + border-color: var(--color-ink); + background: var(--color-surface-2); +} +.bw-map__label { + font-size: 12px; + font-weight: 500; + color: var(--color-ink); +} +.bw-map__val { + font-family: var(--font-mono); + font-size: 11px; + color: var(--color-ink-3); + white-space: nowrap; +} + +/* ── Install race lanes ── */ +.bw-lanes { + display: grid; + grid-template-columns: 1fr; + gap: 0.75rem; +} +@media (min-width: 720px) { + .bw-lanes { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } +} +.bw-lane { + border: 1px solid var(--color-rule); + border-radius: 10px; + padding: 0.75rem 0.85rem 0.6rem; + display: flex; + flex-direction: column; + transition: border-color 0.2s ease; +} +.bw-lane[data-done="true"] { + border-color: var(--color-rule-strong); +} +.bw-lane__head { + display: flex; + align-items: baseline; + gap: 0.5rem; + margin-bottom: 0.65rem; +} +.bw-lane__title { + font-family: var(--font-mono); + font-size: 12px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.bw-lane__sub { + font-size: 11.5px; + color: var(--color-ink-3); +} +.bw-lane__steps { + display: flex; + flex-direction: column; + gap: 0.42rem; + flex: 1; +} +.bw-step { + display: flex; + align-items: baseline; + gap: 0.5rem; + transition: opacity 0.25s ease; +} +.bw-step[data-state="off"] { + opacity: 0.32; +} +.bw-step__dot { + width: 7px; + height: 7px; + border-radius: 999px; + flex-shrink: 0; + border: 1.5px solid var(--color-rule-strong); + background: white; + align-self: center; +} +.bw-step[data-state="on"] .bw-step__dot, +.bw-step[data-state="done"] .bw-step__dot { + background: var(--color-ink); + border-color: var(--color-ink); +} +.bw-step[data-state="bad"] .bw-step__dot { + background: white; + border-color: var(--color-ink); +} +.bw-step__text { + font-family: var(--font-mono); + font-size: 11.5px; + color: var(--color-ink-2); + min-width: 0; +} +.bw-step[data-state="bad"] .bw-step__text { + text-decoration: underline; + text-decoration-style: wavy; + text-decoration-color: var(--color-ink-3); + text-underline-offset: 3px; +} +.bw-step__ok { + margin-left: auto; + font-family: var(--font-mono); + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--color-ink); + border: 1px solid var(--color-rule-strong); + border-radius: 4px; + padding: 0 4px; + white-space: nowrap; +} +.bw-lane__tally { + margin-top: 0.6rem; + padding-top: 0.5rem; + border-top: 1px solid var(--color-rule); + font-family: var(--font-mono); + font-size: 10.5px; + color: var(--color-ink-3); + min-height: 1.6em; +} + +@media (prefers-reduced-motion: reduce) { + .bw-pane, + .bw-step, + .bw-seg__btn, + .bw-btn, + .bw-map__cell, + .bw-lane { + transition: none; + } + .bw-fade-in { + animation: none; + } +} diff --git a/bun.lock b/bun.lock index 46d91d7cee..2eb065d532 100644 --- a/bun.lock +++ b/bun.lock @@ -330,10 +330,11 @@ "version": "0.0.5", "dependencies": { "@astrojs/cloudflare": "^13.0.0", + "@astrojs/mdx": "^6.0.3", "@astrojs/react": "^5.0.4", "@executor-js/react": "workspace:*", "@tailwindcss/vite": "^4.2.2", - "astro": "^6.1.3", + "astro": "^6.4.8", "clsx": "^2.1.1", "motion": "^12.38.0", "posthog-js": "^1.372.5", @@ -1312,17 +1313,19 @@ "@astrojs/cloudflare": ["@astrojs/cloudflare@13.1.10", "", { "dependencies": { "@astrojs/internal-helpers": "0.8.0", "@astrojs/underscore-redirects": "1.0.3", "@cloudflare/vite-plugin": "^1.25.6", "piccolore": "^0.1.3", "tinyglobby": "^0.2.15", "vite": "^7.3.1" }, "peerDependencies": { "astro": "^6.0.0", "wrangler": "^4.61.1" } }, "sha512-Ogl8p4MifPMHbpHKJL78eqEL+I3wbHrYmo83P/FkKZQ9VzzKi2A1RjnbaiiPAwrA8xQOqIUo4zlN7+Mse0aJAQ=="], - "@astrojs/compiler": ["@astrojs/compiler@3.0.1", "", {}, "sha512-z97oYbdebO5aoWzuJ/8q5hLK232+17KcLZ7cJ8BCWk6+qNzVxn/gftC0KzMBUTD8WAaBkPpNSQK6PXLnNrZ0CA=="], + "@astrojs/compiler": ["@astrojs/compiler@4.0.0", "", {}, "sha512-eouss7G8ygdZqHuke033VMcVw5HTZUu+PXd/h06DGDUg/jt5btPYPqh66ENWw/mU78rBrf/oeC4oqoBwMtDMNA=="], "@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.8.0", "", { "dependencies": { "picomatch": "^4.0.3" } }, "sha512-J56GrhEiV+4dmrGLPNOl2pZjpHXAndWVyiVDYGDuw6MWKpBSEMLdFxHzeM/6sqaknw9M+HFfHZAcvi3OfT3D/w=="], - "@astrojs/markdown-remark": ["@astrojs/markdown-remark@7.1.0", "", { "dependencies": { "@astrojs/internal-helpers": "0.8.0", "@astrojs/prism": "4.0.1", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "js-yaml": "^4.1.1", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "retext-smartypants": "^6.2.0", "shiki": "^4.0.0", "smol-toml": "^1.6.0", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.1.0", "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "sha512-P+HnCsu2js3BoTc8kFmu+E9gOcFeMdPris75g+Zl4sY8+bBRbSQV6xzcBDbZ27eE7yBGEGQoqjpChx+KJYIPYQ=="], + "@astrojs/markdown-remark": ["@astrojs/markdown-remark@7.2.0", "", { "dependencies": { "@astrojs/internal-helpers": "0.10.0", "@astrojs/prism": "4.0.2", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.1.0", "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "sha512-+YxmVQu1Bd+MFfSzjq1rOJvD9+nIOJzz5YIIhdIH01RrxRkKbyKoEgyIqP3yv51MhzMDgd79QaPv+kCVPT8vHw=="], - "@astrojs/prism": ["@astrojs/prism@4.0.1", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-nksZQVjlferuWzhPsBpQ1JE5XuKAf1id1/9Hj4a9KG4+ofrlzxUUwX4YGQF/SuDiuiGKEnzopGOt38F3AnVWsQ=="], + "@astrojs/mdx": ["@astrojs/mdx@6.0.3", "", { "dependencies": { "@astrojs/internal-helpers": "0.10.0", "@astrojs/markdown-remark": "7.2.0", "@mdx-js/mdx": "^3.1.1", "acorn": "^8.16.0", "es-module-lexer": "^2.0.0", "estree-util-visit": "^2.0.0", "hast-util-to-html": "^9.0.5", "piccolore": "^0.1.3", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", "remark-smartypants": "^3.0.2", "source-map": "^0.7.6", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3" }, "peerDependencies": { "@astrojs/markdown-satteri": "0.3.0", "astro": "^6.4.0" }, "optionalPeers": ["@astrojs/markdown-satteri"] }, "sha512-+4P3ZvwsRAqAbBgY+uZMewFo3ficlIBPZfu/Luk+v4ia/ZOuFhpsw7r+7672uT2Fc1UPdp7yW0eU5egvSq0wbw=="], + + "@astrojs/prism": ["@astrojs/prism@4.0.2", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA=="], "@astrojs/react": ["@astrojs/react@5.0.4", "", { "dependencies": { "@astrojs/internal-helpers": "0.9.0", "@vitejs/plugin-react": "^5.2.0", "devalue": "^5.6.4", "ultrahtml": "^1.6.0", "vite": "^7.3.2" }, "peerDependencies": { "@types/react": "^17.0.50 || ^18.0.21 || ^19.0.0", "@types/react-dom": "^17.0.17 || ^18.0.6 || ^19.0.0", "react": "^17.0.2 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0" } }, "sha512-yDNE4VnKOzCjH9dCBi7pT4F6kpI3M9TkS+uxnCB0sGIS6t5vKonOY+Hs/UUnSajJGT5jeBRfpI9IQp+r/n1fBA=="], - "@astrojs/telemetry": ["@astrojs/telemetry@3.3.0", "", { "dependencies": { "ci-info": "^4.2.0", "debug": "^4.4.0", "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^3.0.0", "is-wsl": "^3.1.0", "which-pm-runs": "^1.1.0" } }, "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ=="], + "@astrojs/telemetry": ["@astrojs/telemetry@3.3.2", "", { "dependencies": { "ci-info": "^4.4.0", "dset": "^3.1.4", "is-docker": "^4.0.0", "is-wsl": "^3.1.1", "which-pm-runs": "^1.1.0" } }, "sha512-j8DNruA8ors99Al39RYZPJK4DC1bKkoNm93mAMuBhY9TCNC4R8n1q7ovFnJ5qhGh5Lsh7pa1gpQVpYpsJPeTHQ=="], "@astrojs/underscore-redirects": ["@astrojs/underscore-redirects@1.0.3", "", {}, "sha512-cxnGSw+sJigBLdX4TMSZKkzV6C3gMLJMucDk2W+n281Xhie68T2/9f1+1NMNDCZsc5i0FED7Qt5I10g2O9wtZg=="], @@ -3366,7 +3369,7 @@ "astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="], - "astro": ["astro@6.1.7", "", { "dependencies": { "@astrojs/compiler": "^3.0.1", "@astrojs/internal-helpers": "0.8.0", "@astrojs/markdown-remark": "7.1.0", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^4.0.0", "@clack/prompts": "^1.1.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "ci-info": "^4.4.0", "clsx": "^2.1.1", "common-ancestor-path": "^2.0.0", "cookie": "^1.1.1", "devalue": "^5.6.3", "diff": "^8.0.3", "dset": "^3.1.4", "es-module-lexer": "^2.0.0", "esbuild": "^0.27.3", "flattie": "^1.1.1", "fontace": "~0.4.1", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "js-yaml": "^4.1.1", "magic-string": "^0.30.21", "magicast": "^0.5.2", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "obug": "^2.1.1", "p-limit": "^7.3.0", "p-queue": "^9.1.0", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.3", "rehype": "^13.0.2", "semver": "^7.7.4", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "svgo": "^4.0.1", "tinyclip": "^0.1.12", "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15", "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", "unifont": "~0.7.4", "unist-util-visit": "^5.1.0", "unstorage": "^1.17.4", "vfile": "^6.0.3", "vite": "^7.3.1", "vitefu": "^1.1.2", "xxhash-wasm": "^1.1.0", "yargs-parser": "^22.0.0", "zod": "^4.3.6" }, "optionalDependencies": { "sharp": "^0.34.0" }, "bin": { "astro": "bin/astro.mjs" } }, "sha512-pvZysIUV2C2nRv8N7cXAkCLcfDQz/axAxF09SqiTz1B+xnvbhy6KzL2I6J15ZBXk8k0TfMD75dJ151QyQmAqZA=="], + "astro": ["astro@6.4.8", "", { "dependencies": { "@astrojs/compiler": "^4.0.0", "@astrojs/internal-helpers": "0.10.0", "@astrojs/markdown-remark": "7.2.0", "@astrojs/telemetry": "3.3.2", "@capsizecss/unpack": "^4.0.0", "@clack/prompts": "^1.1.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "ci-info": "^4.4.0", "clsx": "^2.1.1", "common-ancestor-path": "^2.0.0", "cookie": "^1.1.1", "devalue": "^5.8.1", "diff": "^8.0.3", "dset": "^3.1.4", "es-module-lexer": "^2.0.0", "esbuild": "^0.27.3", "flattie": "^1.1.1", "fontace": "~0.4.1", "get-tsconfig": "5.0.0-beta.4", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "js-yaml": "^4.1.1", "jsonc-parser": "^3.3.1", "magic-string": "^0.30.21", "magicast": "^0.5.2", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "obug": "^2.1.1", "p-limit": "^7.3.0", "p-queue": "^9.1.0", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.4", "rehype": "^13.0.2", "semver": "^7.7.4", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "svgo": "^4.0.1", "tinyclip": "^0.1.12", "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15", "ultrahtml": "^1.6.0", "unifont": "~0.7.4", "unist-util-visit": "^5.1.0", "unstorage": "^1.17.5", "vfile": "^6.0.3", "vite": "^7.3.2", "vitefu": "^1.1.2", "xxhash-wasm": "^1.1.0", "yargs-parser": "^22.0.0", "zod": "^4.3.6" }, "optionalDependencies": { "sharp": "^0.34.0" }, "bin": { "astro": "./bin/astro.mjs" } }, "sha512-KK5lX90uU9EeVaTjINyj3sy9/NFXVa59aowaqbWBDDKLXZh4rr7GwIaCFYVetE22MJtsCNFerQXn0vlCLmpP/Q=="], "async": ["async@2.6.4", "", { "dependencies": { "lodash": "^4.17.14" } }, "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA=="], @@ -3808,8 +3811,6 @@ "dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="], - "dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="], - "dmg-builder": ["dmg-builder@26.8.1", "", { "dependencies": { "app-builder-lib": "26.8.1", "builder-util": "26.8.1", "fs-extra": "^10.1.0", "iconv-lite": "^0.6.2", "js-yaml": "^4.1.0" }, "optionalDependencies": { "dmg-license": "^1.0.11" } }, "sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg=="], "dmg-license": ["dmg-license@1.0.11", "", { "dependencies": { "@types/plist": "^3.0.1", "@types/verror": "^1.10.3", "ajv": "^6.10.0", "crc": "^3.8.0", "iconv-corefoundation": "^1.1.7", "plist": "^3.0.4", "smart-buffer": "^4.0.2", "verror": "^1.10.0" }, "os": "darwin", "bin": { "dmg-license": "bin/dmg-license.js" } }, "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q=="], @@ -4328,7 +4329,7 @@ "is-descriptor": ["is-descriptor@1.0.3", "", { "dependencies": { "is-accessor-descriptor": "^1.0.1", "is-data-descriptor": "^1.0.1" } }, "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw=="], - "is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], + "is-docker": ["is-docker@4.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA=="], "is-electron": ["is-electron@2.2.2", "", {}, "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg=="], @@ -5614,8 +5615,6 @@ "ts-md5": ["ts-md5@2.0.1", "", {}, "sha512-yF35FCoEOFBzOclSkMNEUbFQZuv89KEQ+5Xz03HrMSGUGB1+r+El+JiGOFwsP4p9RFNzwlrydYoTLvPOuICl9w=="], - "tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="], - "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "tsup": ["tsup@8.5.1", "", { "dependencies": { "bundle-require": "^5.1.0", "cac": "^6.7.14", "chokidar": "^4.0.3", "consola": "^3.4.0", "debug": "^4.4.0", "esbuild": "^0.27.0", "fix-dts-default-cjs-exports": "^1.0.0", "joycon": "^3.1.1", "picocolors": "^1.1.1", "postcss-load-config": "^6.0.1", "resolve-from": "^5.0.0", "rollup": "^4.34.8", "source-map": "^0.7.6", "sucrase": "^3.35.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.11", "tree-kill": "^1.2.2" }, "peerDependencies": { "@microsoft/api-extractor": "^7.36.0", "@swc/core": "^1", "postcss": "^8.4.12", "typescript": ">=4.5.0" }, "optionalPeers": ["@microsoft/api-extractor", "@swc/core", "postcss", "typescript"], "bin": { "tsup": "dist/cli-default.js", "tsup-node": "dist/cli-node.js" } }, "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing=="], @@ -5864,6 +5863,14 @@ "@astrojs/cloudflare/vite": ["vite@7.3.2", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg=="], + "@astrojs/markdown-remark/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.10.0", "", { "dependencies": { "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", "js-yaml": "^4.1.1", "picomatch": "^4.0.4", "retext-smartypants": "^6.2.0", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "unified": "^11.0.5" } }, "sha512-Ry2R3VPeIN4uPCSA4xQc+e+vsJXkalKpEbDc07hV+a/o5Bs2N/s/uDcPJH/05L19DKh9tAy7e6JM3YZ6Cxfezw=="], + + "@astrojs/mdx/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.10.0", "", { "dependencies": { "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", "js-yaml": "^4.1.1", "picomatch": "^4.0.4", "retext-smartypants": "^6.2.0", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "unified": "^11.0.5" } }, "sha512-Ry2R3VPeIN4uPCSA4xQc+e+vsJXkalKpEbDc07hV+a/o5Bs2N/s/uDcPJH/05L19DKh9tAy7e6JM3YZ6Cxfezw=="], + + "@astrojs/mdx/acorn": ["acorn@8.18.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ=="], + + "@astrojs/mdx/es-module-lexer": ["es-module-lexer@2.3.1", "", {}, "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA=="], + "@astrojs/react/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.9.0", "", { "dependencies": { "picomatch": "^4.0.4" } }, "sha512-GdYkzR26re8izmyYlBqf4z2s7zNngmWLFuxw0UKiPNqHraZGS6GKWIwSHgS22RDlu2ePFJ8bzmpBcUszut/SDg=="], "@astrojs/react/@vitejs/plugin-react": ["@vitejs/plugin-react@5.2.0", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw=="], @@ -6434,14 +6441,22 @@ "app-builder-lib/which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="], - "astro/@clack/prompts": ["@clack/prompts@1.2.0", "", { "dependencies": { "@clack/core": "1.2.0", "fast-string-width": "^1.1.0", "fast-wrap-ansi": "^0.1.3", "sisteransi": "^1.0.5" } }, "sha512-4jmztR9fMqPMjz6H/UZXj0zEmE43ha1euENwkckKKel4XpSfokExPo5AiVStdHSAlHekz4d0CA/r45Ok1E4D3w=="], + "astro/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.10.0", "", { "dependencies": { "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", "js-yaml": "^4.1.1", "picomatch": "^4.0.4", "retext-smartypants": "^6.2.0", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "unified": "^11.0.5" } }, "sha512-Ry2R3VPeIN4uPCSA4xQc+e+vsJXkalKpEbDc07hV+a/o5Bs2N/s/uDcPJH/05L19DKh9tAy7e6JM3YZ6Cxfezw=="], - "astro/package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="], + "astro/devalue": ["devalue@5.9.1", "", {}, "sha512-+17vil3EVQRzvtDJSFuTWEb8XJRvXqAiV3qZyQWD398QeXUa6CxsUyMdD1fxzEhUrd4FojitFz7lhIHBTlV4fw=="], + + "astro/es-module-lexer": ["es-module-lexer@2.3.1", "", {}, "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA=="], + + "astro/get-tsconfig": ["get-tsconfig@5.0.0-beta.4", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ=="], - "astro/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "astro/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], + + "astro/package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="], "astro/vite": ["vite@7.3.2", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg=="], + "astro/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + "atlas-api-client/rxjs": ["rxjs@5.5.12", "", { "dependencies": { "symbol-observable": "1.0.1" } }, "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw=="], "atmn/commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], @@ -6594,6 +6609,8 @@ "ink-confirm-input/ink-text-input": ["ink-text-input@3.3.0", "", { "dependencies": { "chalk": "^3.0.0", "prop-types": "^15.5.10" }, "peerDependencies": { "ink": "^2.0.0", "react": "^16.5.2" } }, "sha512-gO4wrOf2ie3YuEARTIwGlw37lMjFn3Gk6CKIDrMlHb46WFMagZU7DplohjM24zynlqfnXA5UDEIfC2NBcvD8kg=="], + "is-inside-container/is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], + "jake/async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="], "jsonwebtoken/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], @@ -6798,6 +6815,10 @@ "yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + "@astrojs/markdown-remark/@astrojs/internal-helpers/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], + + "@astrojs/mdx/@astrojs/internal-helpers/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], + "@astrojs/react/@vitejs/plugin-react/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="], "@azure/identity/@azure/msal-node/@azure/msal-common": ["@azure/msal-common@16.4.1", "", {}, "sha512-Bl8f+w37xkXsYh7QRkAKCFGYtWMYuOVO7Lv+BxILrvGz3HbIEF22Pt0ugyj0QPOl6NLrHcnNUQ9yeew98P/5iw=="], @@ -7266,12 +7287,6 @@ "app-builder-lib/which/isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="], - "astro/@clack/prompts/@clack/core": ["@clack/core@1.2.0", "", { "dependencies": { "fast-wrap-ansi": "^0.1.3", "sisteransi": "^1.0.5" } }, "sha512-qfxof/3T3t9DPU/Rj3OmcFyZInceqj/NVtO9rwIuJqCUgh32gwPjpFQQp/ben07qKlhpwq7GzfWpST4qdJ5Drg=="], - - "astro/@clack/prompts/fast-string-width": ["fast-string-width@1.1.0", "", { "dependencies": { "fast-string-truncated-width": "^1.2.0" } }, "sha512-O3fwIVIH5gKB38QNbdg+3760ZmGz0SZMgvwJbA1b2TGXceKE6A2cOlfogh1iw8lr049zPyd7YADHy+B7U4W9bQ=="], - - "astro/@clack/prompts/fast-wrap-ansi": ["fast-wrap-ansi@0.1.6", "", { "dependencies": { "fast-string-width": "^1.1.0" } }, "sha512-HlUwET7a5gqjURj70D5jl7aC3Zmy4weA1SHUfM0JFI0Ptq987NH2TwbBFLoERhfwk+E+eaq4EK3jXoT+R3yp3w=="], - "broadcast-channel/p-queue/eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], "broadcast-channel/p-queue/p-timeout": ["p-timeout@3.2.0", "", { "dependencies": { "p-finally": "^1.0.0" } }, "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg=="], @@ -7766,8 +7781,6 @@ "agents/yargs/string-width/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], - "astro/@clack/prompts/fast-string-width/fast-string-truncated-width": ["fast-string-truncated-width@1.2.1", "", {}, "sha512-Q9acT/+Uu3GwGj+5w/zsGuQjh9O1TyywhIwAxHudtWrgF09nHOPrvTLhQevPbttcxjr/SNN7mJmfOw/B1bXgow=="], - "dir-compare/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "filelist/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],