Zum Inhalt springen
Blume
Esc
↑↓navigieren↵öffnen⌘Jvorschau
Auf dieser Seite

Anpassung

Überschreibe Komponenten, füge interaktive Islands hinzu, binde eigene Seiten ein, installiere Registry-Komponenten oder ejecte vollständig, wenn du volle Kontrolle brauchst.

Komponenten-Overrides

Füge eine components.ts (oder components.tsx) im Stammverzeichnis deines Projekts hinzu und exportiere defineComponents. Die mdx-Map ersetzt entweder eine eingebaute Komponente oder fügt eine neue hinzu — verfügbar in jeder .mdx-Seite ohne Import.

import { defineComponents } from "blume";
import Callout from "./components/Callout.astro";
import Pricing from "./components/Pricing.astro";

export default defineComponents({
  mdx: {
    Callout, // replace the built-in Callout
    Pricing, // add a new <Pricing /> component
  },
});

Die Schlüssel sind die Namen, die du in MDX schreibst (<Callout>, <Pricing>). Verwende den Dateinamen mit .tsx, wenn du React-Komponenten importierst.

Ein großgeschriebenes Tag, das weder eine eingebaute Komponente noch ein Island noch ein Override ist, lässt die Seite beim Rendern fehlschlagen. Deshalb warnen blume dev, blume build und blume check schon vorher mit BLUME_UNKNOWN_COMPONENT davor — einmal pro Tag-Name, und sie nennen dabei jede Seite, die es verwendet.

Referenzformen

Jedes Override — in mdx oder layout — akzeptiert drei Formen:

import { defineComponents } from "blume";
import Callout from "./components/Callout.astro";

export default defineComponents({
  mdx: {
    Callout, // 1. an imported component
    Note: "./components/Note.astro", // 2. a path string (resolved from the project root)
    Chart: { component: "./components/Chart.tsx", client: "load" }, // 3. a descriptor
  },
});

Blume liest components.ts statisch — es führt die Datei nie aus —, daher sind diese drei Formen die einzigen, die es akzeptiert. Eine Inline-Funktion oder ein Ausdruck, eine in der Datei selbst deklarierte Komponente, ein Spread, ein berechneter Schlüssel oder ein client, der keiner der unten aufgeführten Modus-Strings ist, führt zu einem BLUME_COMPONENTS_INVALID-Fehler, der den Eintrag benennt: blume dev meldet ihn im Terminal und im Browser-Overlay, und blume build schlägt fehl.

Die Descriptor-Form ergänzt einen Hydration-Modus, damit eine interaktive React-/Vue-/Svelte-Komponente ihr JavaScript ausliefert und auf dem Client zum Leben erwacht. Ohne einen client-Modus wird eine Framework-Komponente als statisches HTML gerendert — Blume gibt eine Build-Warnung aus, wenn es so einen Fall entdeckt, da das meist ein Versehen ist.

client Hydratisiert
"load" Sofort beim Laden der Seite
"idle" Wenn der Main Thread im Leerlauf ist
"visible" Wenn sie in den sichtbaren Bereich gescrollt wird
"media" Wenn eine media-Query zutrifft (ergänze media: "(min-width: 40rem)")
"only" Nur auf dem Client, niemals serverseitig gerendert

Ein mdx-Eintrag mit einem client-Modus ist ein Island: Er ist in jeder Seite verfügbar und hydratisiert genau wie eine Komponente, die du in den islands/-Ordner legst. Der Ordner bleibt der Weg ganz ohne Konfiguration — nimm einen Eintrag in components.ts, wenn du einen anderen Namen als den Dateinamen, eine media-Query oder deine Islands neben deinen anderen Overrides haben möchtest.

Ein Override typisieren

Wenn du eine eingebaute Komponente ersetzt, importiere ihren Prop-Typ aus blume/components, damit deine Komponente dem Vertrag entspricht — die Typen werden aus den Komponenten selbst abgeleitet, laufen also nie auseinander:

import type { CalloutProps } from "blume/components";

export default function Callout(props: CalloutProps) {
  // …your own callout, same props as the built-in
}

Prop-Typen werden für die Content-Komponenten exportiert (CalloutProps, CardProps, TabsProps, StepsProps, BadgeProps und weitere).

Layout-Slots

Die layout-Map ersetzt einen Teil von Blumes Rahmen durch deine eigene Komponente. Jedes Override erhält dieselben Props wie die eingebaute Komponente, die es ersetzt, sodass du die Standardvariante umhüllen oder bei null anfangen kannst.

import { defineComponents } from "blume";
import Footer from "./components/Footer.astro";
import Logo from "./components/Logo.astro";

export default defineComponents({
  layout: {
    Logo, // brand mark + title in the header
    Footer, // site-wide footer, in place of the one `footer` configures
  },
});

Verdrahtete Slots (bei den Namen wird zwischen Groß- und Kleinschreibung unterschieden; jeder andere Schlüssel unter layout wird nirgends gerendert, und Blume warnt davor):

Slot Ersetzt Props
Layout Die gesamte Seitenhülle (RootLayout) Alles, was das eingebaute Layout erhält, plus die layout-Map
Header Die obere Navigationsleiste site, logo, navigation, route, searchEnabled, …
Logo Der Marken-Link (Bildmarke + Titel) im Header site, logo, locale
Search Der Suchauslöser im Header + Modal navigation, strings, locale, assistantEnabled
Sidebar Der primäre Navigationsbaum items, currentRoute
MobileNav Die Navigation in der mobilen Schublade (standardmäßig Sidebar) items, currentRoute
Breadcrumbs Der Breadcrumb-Pfad crumbs
TableOfContents Die Gliederung „Auf dieser Seite“, in der rechten Leiste (variant: "desktop") und im Dropdown über dem Inhalt auf schmaleren Bildschirmen ("mobile") headings, title, variant
Pagination Die Zurück-/Weiter-Links im Fußbereich prev, next, strings
Feedback Die Bewertung „War diese Seite hilfreich?“ unter dem Artikel (wird nur gerendert, wenn feedback aktiviert ist) strings, comments
PageHeader Ein Einfügepunkt über dem Artikel (keine eingebaute Komponente) page, headings, route
PageFooter Ein Einfügepunkt unter dem Artikel (keine eingebaute Komponente) page, headings, route
Footer Der seitenweite Footer nach dem Content-Grid: Links, Social-Media-Profile, der Repository-Link und die Cookie-Einstellungen footer, locale, site, navigation, ui

Die mobile-Variante erhält außerdem die Seitenaktionen als Children (ein leeres Element, in das die Aktionen unterhalb von 1.280px aus der rechten Leiste verschoben werden), rendere darin also einen <slot />, damit sie auf schmaleren Bildschirmen erhalten bleiben. Sie wird auch auf einer Seite ohne Überschriften gerendert, dann mit leerem headings, sodass die Aktionen auch dort angezeigt werden.

PageHeader und PageFooter haben keine eingebaute Komponente — sie rendern nichts, bis du sie setzt, was sie zu praktischen Einfügepunkten für ein Werbebanner oder einen „Zuletzt aktualisiert“-Hinweis macht. PageFooter wird am Fuß jeder Seite gerendert, die Blume aus deinem Content baut, API-Operationsseiten eingeschlossen, gefolgt von Feedback und Pagination. Eine Seite im custom- oder frame-Modus lässt diesen Bereich am Seitenende weg, zusammen mit ihrem Titel und ihren Breadcrumbs. Eine eigene Seite unter pages/ rendert das Layout, in das sie sich selbst einbettet: RootLayout nimmt Slot-Overrides über seine layout-Prop entgegen, und PageLayout hat keinen Bereich am Seitenende. Der eingebaute Footer enthält die einzigen Links zu deinem Repository und deinen Social-Media-Profilen, daher sollte ein Footer-Override (der Ort für einen Marketing-Footer, den die Konfiguration nicht abbilden kann) sie beibehalten. Wenn consent gesetzt ist, behalte außerdem eine Möglichkeit bei, die Einwilligungsabfrage erneut zu öffnen: Jedes Element mit data-blume-consent-open funktioniert als Link zu den Cookie-Einstellungen.

Layout-Slots akzeptieren dieselben drei Referenzformen wie MDX-Overrides, sodass ein Slot ein Pfad-String oder ein hydratisierter Descriptor ({ component, client }) sein kann, wenn du einen interaktiven Header oder Footer möchtest.

Interaktive Islands

Für interaktive UI (React, Vue oder Svelte) legst du eine Komponente in einen islands/-Ordner und verwendest sie in jeder MDX-Seite — Blume hydratisiert sie für dich, ohne Wrapper oder Registrierung:

import { useState } from "react";

export default function Counter() {
  const [n, setN] = useState(0);
  return <button onClick={() => setN(n + 1)}>Clicked {n}</button>;
}
Use it anywhere: <Counter />

Siehe Islands für Hydration-Strategien und Framework-Setup.

Eigene Seiten

Füge .astro-Dateien in deinem pages/-Ordner hinzu, um vollständig eigene Routen neben deiner Dokumentation einzubinden — eine Landingpage, eine Preisseite oder einen handgebauten Index. Sie behalten ihren Ort, sodass relative Importe und getStaticPaths wie gewohnt funktionieren, und sie können deine Konfiguration, Navigation und Routen aus dem Modul blume:data lesen.

Siehe Eigene Seiten für die vollständige Anleitung.

Registry

blume add kopiert eine von Blume gepflegte Komponente als Quellcode in dein Projekt — sie gehört dir und du kannst sie frei bearbeiten. Führe den Befehl ohne Argumente aus, um aufzulisten, was verfügbar ist:

blume add

Installiere einen Layout-Slot (Header, Sidebar, Breadcrumbs, Inhaltsverzeichnis, Pagination oder Feedback) oder eine beliebige Content-Komponente (Callout, Card, Tabs, Steps, Accordion und weitere):

blume add callout
blume add pagination

Die Kopie importiert den Rest des Frameworks aus blume/*, sodass sie exakt wie die eingebaute Komponente rendert, bis du sie änderst. blume add gibt das defineComponents-Snippet aus, um sie zu registrieren — Content-Komponenten unter mdx, Layout-Teile unter layout.

Astro-Integrationen

Füge eine beliebige Astro-Integration über das Array integrations auf oberster Ebene in blume.config.ts hinzu. Installiere die Integration zuerst in deiner Site; Blume fügt sie nicht zu den Abhängigkeiten der generierten Runtime hinzu und verwaltet auch nicht ihre Astro-Kompatibilität.

npm install @astrojs/partytown
import partytown from "@astrojs/partytown";
import { defineConfig } from "blume";

export default defineConfig({
  integrations: [
    partytown({
      config: { forward: ["dataLayer.push"] },
    }),
  ],
});

Blume behält seine eingebauten Integrationen in ihrer bestehenden Reihenfolge und hängt anschließend deine Einträge in Deklarationsreihenfolge an. Sie werden weder sortiert noch dedupliziert, sodass zwei Integrationen mit demselben name beide laufen. Blume validiert, dass integrations ein Array ist, während Astro jeden Eintrag validiert und ungültige Integrationen meldet.

Da Blume deine Integrationen lädt, indem es blume.config.ts aus der generierten Astro-Konfiguration erneut importiert, statt die Instanzen zu kopieren, wird das Konfigurationsmodul pro Lauf zweimal ausgewertet — einmal, wenn Blume deine Konfiguration liest, und einmal, wenn Astro sie lädt. Halte Integrations-Factories frei von Seiteneffekten (gib die Integration zurück; schreibe keine Dateien und öffne keine Verbindungen bei der Konstruktion), damit die zweite Auswertung harmlos bleibt.

Dieselben Integrationen laufen in blume dev und blume build. Wenn du blume.config.ts während blume dev bearbeitest, wird die versteckte Astro-Konfiguration neu generiert und ein Konfigurationsneustart ausgelöst; falls eine bearbeitete Integration keine Wirkung zeigt, starte blume dev neu. Blume kann nicht erkennen, welche Konfigurationsänderungen Integrationen betreffen, daher löst jede Änderung an blume.config.ts — selbst an einem nicht zusammenhängenden Feld — einen Neustart des Dev-Servers aus statt einer Hot-Anwendung, sobald integrations nicht leer ist. Blume verfolgt nur den Inhalt von blume.config.ts, sodass das Bearbeiten einer separaten Datei, die davon importiert wird, diese Neugenerierung nicht von selbst auslöst — starte blume dev nach solchen Änderungen neu. Wenn du ejectest, behält die dir gehörende astro.config.mjs eine relative Brücke zu blume.config.ts, sodass die konfigurierten Integrationen weiterhin laufen; du kannst sie später direkt in die Astro-Konfiguration verschieben, als Teil der vollständigen Übernahme.

Eject

Wenn du volle Kontrolle möchtest, ejecte die generierte Runtime in ein eigenständiges Astro-Projekt:

npx blume eject --yes

Eject ist ein einseitiger Schritt: Die versteckte .blume/-Runtime wird zu einer normalen Astro-App, die dir gehört und die du direkt verändern kannst. Das Paket blume bleibt importierbar, sodass du seine Komponenten, sein Theme und seine Markdown-Prozessoren behältst.

Eject stellt die Skripte in deiner package.json auf astro dev und astro build um und fügt die Pakete hinzu, die die Astro-App namentlich importiert — astro, @tailwindcss/vite, tailwindcss und @tailwindcss/typography für die generierten Stylesheets, den Such-Client, die Integrationen und den Adapter, die die Konfiguration einbindet, React, wenn ein Island, ein Beispiel oder der Assistent es verwendet, ai für die Assistenten-Route und epub-gen-memory für den EPUB-Export — jeweils in den Versionsbereichen, die Blume selbst verwendet. Führe vor dev oder build eine Installation aus; Eject listet auf, was es hinzugefügt hat. Jeder Pfad in der ge-ejecteten App ist relativ, sodass sie aus jedem Checkout heraus baut, CI eingeschlossen.

Ab dann startest du die App über ihre eigenen Skripte (npm run dev, npm run build): blume dev, blume build, blume check, blume sync und blume preview brechen in einem ge-ejecteten Projekt ab und verweisen dich dorthin. Auch ein erneutes blume eject wird verweigert, da es deine Änderungen überschreiben würde; übergib --force, um die App trotzdem neu zu generieren.

Was Eject beibehält

Die Routen, die die versteckte Runtime als Seiten ausliefert, werden in das src/pages der App geschrieben, sodass sie unter denselben URLs antwortet: das Markdown-Pendant jeder Seite, die 404-Seite mit ihren Pendants /404.md und /404.json, der gehostete MCP-Server sowie die JSON-Docs-API unter /api/docs/… und /openapi.json, auf die llms.txt und die 404-Seite Agents verweisen.

Das build-Skript der ge-ejecteten App führt einfach astro build aus, und die Artefakte, die blume build obendrauf legt — der Suchindex (und die Index-Synchronisierung eines gehosteten Anbieters), llms.txt und llms-full.txt, sitemap.xml, robots.txt, agent-readability.json, die .well-known-Discovery-Dateien, Agent Skills sowie die Plattform-Dateien _redirects/_headers — werden weiterhin erzeugt: Die Blume-Integration in der ge-ejecteten astro.config.mjs schreibt sie aus Astros astro:build:done-Hook und scannt dabei das Projekt (deine blume.config.ts und deinen Content) genauso, wie es die CLI getan hat. Was der ge-ejectete Build nicht übernimmt, ist die Adapter-Nachbearbeitung der CLI: die Routing-Anpassungen für Accept: text/markdown bei Vercel und Cloudflare, die Prüfung des Vercel-Function-Bundles, der node()-Server-Entry-Wrapper (die Media Types und CORS-Header der .well-known-Discovery-Dateien, die Sandbox für heruntergeladene SVGs und der exakte Status jeder Weiterleitung), die Header-Regeln, die ein netlify()-Server-Build in .netlify/v1/config.json schreibt, die Benennung des Cloudflare-Workers (nach deinem Projekt) und seine .wrangler/deploy-Weiterleitung, um wrangler deploy aus dem Projektstammverzeichnis auszuführen, sowie die Prüfung über --analyze/--budget-*.

Zuletzt aktualisiert am

War diese Seite hilfreich?