कस्टमाइज़ेशन
कंपोनेंट्स को ओवरराइड करें, इंटरैक्टिव आइलैंड्स जोड़ें, कस्टम पेज माउंट करें, रजिस्ट्री कंपोनेंट्स इंस्टॉल करें, या जब आपको पूरा नियंत्रण चाहिए तो पूरी तरह इजेक्ट कर दें।
कंपोनेंट ओवरराइड्स
अपने प्रोजेक्ट रूट में एक components.ts (या components.tsx) जोड़ें और defineComponents एक्सपोर्ट करें। mdx मैप या तो किसी बिल्ट-इन कंपोनेंट को बदल देता है या एक नया कंपोनेंट जोड़ता है — जो बिना किसी इम्पोर्ट के हर .mdx पेज में उपलब्ध रहता है।
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
},
});
कीज़ वही नाम होते हैं जो आप MDX में लिखते हैं (<Callout>, <Pricing>)। जब आप React कंपोनेंट्स इम्पोर्ट करें तो .tsx फ़ाइलनाम का उपयोग करें।
बड़े अक्षर से शुरू होने वाला कोई टैग, जो न तो बिल्ट-इन है, न आइलैंड, और न ही ओवरराइड, रेंडर होते समय पेज को विफल कर देता है, इसलिए blume dev, blume build, और blume check पहले ही इसके बारे में BLUME_UNKNOWN_COMPONENT के रूप में चेतावनी देते हैं — प्रति टैग एक बार, और उन सभी पेजों का नाम बताते हुए जो इसका उपयोग करते हैं।
रेफ़रेंस रूप
हर ओवरराइड — चाहे mdx या layout में हो — तीन रूप स्वीकार करता है:
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 components.ts को स्टैटिक रूप से पढ़ता है — वह फ़ाइल को कभी चलाता नहीं — इसलिए वह केवल यही तीन रूप स्वीकार करता है। कोई इनलाइन फ़ंक्शन या एक्सप्रेशन, फ़ाइल में ही घोषित कोई कंपोनेंट, कोई स्प्रेड, कोई कंप्यूटेड की, या ऐसा client जो नीचे दी गई मोड स्ट्रिंग्स में से एक न हो, एक BLUME_COMPONENTS_INVALID त्रुटि होती है जो संबंधित प्रविष्टि का नाम बताती है: blume dev इसे टर्मिनल और ब्राउज़र ओवरले में रिपोर्ट करता है, और blume build विफल हो जाता है।
डिस्क्रिप्टर रूप एक हाइड्रेशन मोड जोड़ता है ताकि कोई इंटरैक्टिव React/Vue/Svelte कंपोनेंट अपना JavaScript भेजे और क्लाइंट पर जीवंत हो जाए। client मोड के बिना कोई फ़्रेमवर्क कंपोनेंट स्टैटिक HTML के रूप में रेंडर होता है — Blume ऐसा कंपोनेंट मिलने पर बिल्ड चेतावनी प्रिंट करता है, क्योंकि यह आमतौर पर एक गलती होती है।
client |
हाइड्रेट होता है |
|---|---|
"load" |
पेज लोड होते ही तुरंत |
"idle" |
जब मेन थ्रेड निष्क्रिय हो |
"visible" |
जब स्क्रॉल करके दृश्य में आए |
"media" |
जब कोई media क्वेरी मैच करे (media: "(min-width: 40rem)" जोड़ें) |
"only" |
केवल क्लाइंट पर, कभी सर्वर-रेंडर नहीं |
client मोड वाली कोई mdx प्रविष्टि वास्तव में एक आइलैंड है: वह हर पेज में उपलब्ध रहती है और ठीक उसी तरह हाइड्रेट होती है जैसे islands/ फ़ोल्डर में रखा गया कोई कंपोनेंट। फ़ोल्डर ही शून्य-कॉन्फ़िग वाला रास्ता बना रहता है — components.ts प्रविष्टि का उपयोग तब करें जब आप फ़ाइल से अलग कोई नाम, कोई media क्वेरी चाहते हों, या आइलैंड्स को अपने अन्य ओवरराइड्स के साथ रखना चाहते हों।
किसी ओवरराइड को टाइप करना
जब आप किसी बिल्ट-इन को बदलें, तो उसका प्रॉप टाइप blume/components से इम्पोर्ट करें ताकि आपका कंपोनेंट अनुबंध से मेल खाए — ये टाइप्स कंपोनेंट्स से ही व्युत्पन्न होते हैं, इसलिए वे कभी अलग नहीं होते:
import type { CalloutProps } from "blume/components";
export default function Callout(props: CalloutProps) {
// …your own callout, same props as the built-in
}
कंटेंट कंपोनेंट्स के लिए प्रॉप टाइप्स एक्सपोर्ट किए जाते हैं (CalloutProps, CardProps, TabsProps, StepsProps, BadgeProps, और अन्य)।
लेआउट स्लॉट्स
layout मैप Blume के क्रोम के किसी हिस्से को आपके अपने कंपोनेंट से बदल देता है। हर ओवरराइड को वही प्रॉप्स मिलते हैं जो उस बिल्ट-इन को मिलते हैं जिसे वह बदल रहा है, इसलिए आप डिफ़ॉल्ट को रैप कर सकते हैं या शुरू से बना सकते हैं।
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
},
});
जुड़े हुए स्लॉट्स (नाम केस-सेंसिटिव हैं; layout के अंतर्गत कोई भी अन्य की कहीं भी रेंडर नहीं होती, और Blume इसके बारे में चेतावनी देता है):
| स्लॉट | किसे बदलता है | प्रॉप्स |
|---|---|---|
Layout |
पूरा पेज शेल (RootLayout) |
वह सब कुछ जो बिल्ट-इन लेआउट को मिलता है, साथ ही layout मैप |
Header |
ऊपरी नेविगेशन बार | site, logo, navigation, route, searchEnabled, … |
Logo |
हेडर में ब्रांड लिंक (मार्क + शीर्षक) | site, logo, locale |
Search |
हेडर का सर्च ट्रिगर + मोडल | navigation, strings, locale, assistantEnabled |
Sidebar |
प्राथमिक नेविगेशन ट्री | items, currentRoute |
MobileNav |
मोबाइल ड्रॉअर के अंदर का नेव (डिफ़ॉल्ट रूप से Sidebar) |
items, currentRoute |
Breadcrumbs |
ब्रेडक्रम्ब पथ | crumbs |
TableOfContents |
इस पेज पर वाली रूपरेखा, दाईं रेल में (variant: "desktop") और संकरी स्क्रीन पर कंटेंट के ऊपर ड्रॉपडाउन में ("mobile") |
headings, title, variant |
Pagination |
पिछला/अगला फुटर लिंक | prev, next, strings |
Feedback |
लेख के नीचे “क्या यह पेज उपयोगी था?” रेटिंग (केवल तब रेंडर होती है जब feedback चालू हो) |
strings, comments |
PageHeader |
लेख के ऊपर एक इंजेक्शन बिंदु (कोई बिल्ट-इन नहीं) | page, headings, route |
PageFooter |
लेख के नीचे एक इंजेक्शन बिंदु (कोई बिल्ट-इन नहीं) | page, headings, route |
Footer |
कंटेंट ग्रिड के बाद साइट फुटर: लिंक, सोशल प्रोफ़ाइल, रिपॉज़िटरी लिंक, और कुकी सेटिंग्स | footer, locale, site, navigation, ui |
mobile वैरिएंट को पेज ऐक्शन्स भी अपने चिल्ड्रन के रूप में मिलते हैं (एक खाली एलिमेंट, जिसमें 1,280px से कम चौड़ाई पर ऐक्शन्स दाईं रेल से आ जाते हैं), इसलिए संकरी स्क्रीन पर उन्हें बनाए रखने के लिए उसमें एक <slot /> रेंडर करें। यह बिना हेडिंग वाले पेज पर भी रेंडर होता है, जिसमें headings खाली होता है, ताकि ऐक्शन्स वहाँ भी दिखें।
PageHeader और PageFooter का कोई बिल्ट-इन कंपोनेंट नहीं है — जब तक आप उन्हें सेट नहीं करते वे कुछ भी रेंडर नहीं करते, जिससे वे किसी प्रोमो बैनर या “अंतिम बार अपडेट किया गया” नोट के लिए सुविधाजनक इंजेक्शन बिंदु बन जाते हैं। PageFooter उन सभी पेजों के अंत में रेंडर होता है जिन्हें Blume आपके कंटेंट से बनाता है, API ऑपरेशन पेजों सहित, और उसके बाद Feedback और Pagination आते हैं। custom या frame मोड वाला पेज अपने शीर्षक और ब्रेडक्रम्ब्स के साथ-साथ पेज के अंत वाला वह क्षेत्र भी हटा देता है। pages/ के अंतर्गत कोई कस्टम पेज वही लेआउट रेंडर करता है जिसमें वह स्वयं को रैप करता है: RootLayout अपने layout प्रॉप के ज़रिए स्लॉट ओवरराइड्स लेता है, और PageLayout में पेज के अंत वाला कोई क्षेत्र नहीं होता। आपकी रिपॉज़िटरी और सोशल प्रोफ़ाइल के एकमात्र लिंक बिल्ट-इन Footer में ही होते हैं, इसलिए किसी Footer ओवरराइड (ऐसे मार्केटिंग फुटर के लिए सही जगह जिसे कॉन्फ़िग व्यक्त नहीं कर सकता) में उन्हें बनाए रखना चाहिए। consent सेट होने पर, उसे फिर से खोलने का एक तरीका भी बनाए रखें: data-blume-consent-open वाला कोई भी एलिमेंट कुकी सेटिंग्स लिंक के रूप में काम करता है।
लेआउट स्लॉट्स MDX ओवरराइड्स जैसे ही तीन रेफ़रेंस रूप स्वीकार करते हैं, इसलिए जब आप इंटरैक्टिव हेडर या फुटर चाहें तो कोई स्लॉट एक पाथ स्ट्रिंग या हाइड्रेटेड डिस्क्रिप्टर ({ component, client }) हो सकता है।
इंटरैक्टिव आइलैंड्स
इंटरैक्टिव UI (React, Vue, या Svelte) के लिए, किसी कंपोनेंट को islands/ फ़ोल्डर में रखें और उसे किसी भी MDX पेज में उपयोग करें — Blume उसे आपके लिए हाइड्रेट करता है, किसी रैपर या रजिस्ट्रेशन की ज़रूरत नहीं:
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 />
हाइड्रेशन रणनीतियों और फ़्रेमवर्क सेटअप के लिए Islands देखें।
कस्टम पेज
अपने डॉक्स के साथ पूरी तरह कस्टम रूट माउंट करने के लिए अपने pages/ फ़ोल्डर के अंतर्गत .astro फ़ाइलें जोड़ें — एक लैंडिंग पेज, एक प्राइसिंग पेज, या हाथ से बनाया गया इंडेक्स। वे अपना स्थान बनाए रखते हैं, इसलिए रिलेटिव इम्पोर्ट्स और getStaticPaths सामान्य रूप से काम करते हैं, और वे blume:data मॉड्यूल से आपका कॉन्फ़िग, नेविगेशन, और रूट्स पढ़ सकते हैं।
पूरी गाइड के लिए Custom pages देखें।
रजिस्ट्री
blume add किसी Blume-रखरखावित कंपोनेंट को आपके प्रोजेक्ट में सोर्स के रूप में कॉपी करता है — वह आपका होता है और आप उसे स्वतंत्र रूप से संपादित कर सकते हैं। जो उपलब्ध है उसकी सूची देखने के लिए इसे बिना किसी आर्ग्युमेंट के चलाएँ:
blume add
कोई लेआउट स्लॉट (हेडर, साइडबार, ब्रेडक्रम्ब्स, टेबल ऑफ़ कंटेंट्स, पेजिनेशन, या फ़ीडबैक) या कोई भी कंटेंट कंपोनेंट (कॉलआउट, कार्ड, टैब्स, स्टेप्स, अकॉर्डियन, और अन्य) इंस्टॉल करें:
blume add callout
blume add pagination
कॉपी बाक़ी फ़्रेमवर्क को blume/* से इम्पोर्ट करती है, इसलिए जब तक आप उसे बदलते नहीं, वह ठीक बिल्ट-इन की तरह ही रेंडर होती है। blume add उसे रजिस्टर करने के लिए defineComponents स्निपेट प्रिंट करता है — कंटेंट कंपोनेंट्स mdx के अंतर्गत, लेआउट हिस्से layout के अंतर्गत।
Astro इंटीग्रेशन
blume.config.ts में शीर्ष-स्तरीय integrations ऐरे से कोई भी Astro इंटीग्रेशन जोड़ें। पहले इंटीग्रेशन को अपनी साइट में इंस्टॉल करें; Blume उसे जनरेट किए गए रनटाइम की डिपेंडेंसीज़ में नहीं जोड़ता और न ही उसकी Astro संगतता प्रबंधित करता है।
npm install @astrojs/partytown
import partytown from "@astrojs/partytown";
import { defineConfig } from "blume";
export default defineConfig({
integrations: [
partytown({
config: { forward: ["dataLayer.push"] },
}),
],
});
Blume अपने बिल्ट-इन इंटीग्रेशन को उनके मौजूदा क्रम में रखता है, फिर आपकी प्रविष्टियाँ घोषणा क्रम में जोड़ता है। यह उन्हें न तो क्रमबद्ध करता है और न ही डुप्लिकेट हटाता है, इसलिए समान name वाले दो इंटीग्रेशन दोनों चलते हैं। Blume यह सत्यापित करता है कि integrations एक ऐरे है, जबकि Astro हर प्रविष्टि को सत्यापित करता है और अमान्य इंटीग्रेशन की रिपोर्ट देता है।
चूँकि Blume आपके इंटीग्रेशन को इंस्टेंस कॉपी करने के बजाय जनरेट किए गए Astro कॉन्फ़िग से blume.config.ts को फिर से इम्पोर्ट करके लोड करता है, इसलिए कॉन्फ़िग मॉड्यूल प्रति रन दो बार मूल्यांकित होता है — एक बार जब Blume आपका कॉन्फ़िग पढ़ता है और एक बार जब Astro उसे लोड करता है। इंटीग्रेशन फ़ैक्ट्रियों को साइड-इफ़ेक्ट मुक्त रखें (इंटीग्रेशन लौटाएँ; निर्माण के समय फ़ाइलें न लिखें या कनेक्शन न खोलें) ताकि दूसरा मूल्यांकन हानिरहित रहे।
वही इंटीग्रेशन blume dev और blume build दोनों में चलते हैं। blume dev के दौरान blume.config.ts संपादित करने से छिपा हुआ Astro कॉन्फ़िग फिर से जनरेट होता है और कॉन्फ़िग रीस्टार्ट ट्रिगर होता है; यदि आपको संपादित इंटीग्रेशन प्रभावी होता न दिखे, तो blume dev को रीस्टार्ट करें। Blume यह नहीं बता सकता कि कौन-से कॉन्फ़िग संपादन इंटीग्रेशन को प्रभावित करते हैं, इसलिए एक बार integrations ग़ैर-खाली हो जाने पर, blume.config.ts का हर संपादन — यहाँ तक कि किसी असंबंधित फ़ील्ड का भी — हॉट-अप्लाई करने के बजाय डेव सर्वर को रीस्टार्ट करता है। Blume केवल blume.config.ts की सामग्री ट्रैक करता है, इसलिए उसके द्वारा इम्पोर्ट की गई किसी अलग फ़ाइल को संपादित करना अपने आप वह पुनर्जनन ट्रिगर नहीं करेगा — ऐसे संपादनों के बाद blume dev रीस्टार्ट करें। यदि आप इजेक्ट करते हैं, तो आपके स्वामित्व वाला astro.config.mjs blume.config.ts से एक रिलेटिव ब्रिज बनाए रखता है, इसलिए कॉन्फ़िगर किए गए इंटीग्रेशन चलते रहते हैं; बाद में आप पूर्ण स्वामित्व लेने के हिस्से के रूप में उन्हें सीधे Astro कॉन्फ़िग में ले जा सकते हैं।
इजेक्ट
जब आपको पूरा नियंत्रण चाहिए, तो जनरेट किए गए रनटाइम को एक स्वतंत्र Astro प्रोजेक्ट में इजेक्ट करें:
npx blume eject --yes
इजेक्ट एक तरफ़ा कदम है: छिपा हुआ .blume/ रनटाइम एक सामान्य Astro ऐप बन जाता है जिसका स्वामित्व आपके पास होता है और जिसे आप सीधे संशोधित कर सकते हैं। blume पैकेज इम्पोर्ट योग्य बना रहता है, इसलिए आप इसके कंपोनेंट्स, थीम, और Markdown प्रोसेसर बनाए रखते हैं।
इजेक्ट आपकी package.json स्क्रिप्ट्स को astro dev और astro build की ओर निर्देशित करता है, और वे पैकेज जोड़ता है जिन्हें Astro ऐप नाम से इम्पोर्ट करता है — astro, @tailwindcss/vite, जनरेट की गई स्टाइलशीट्स के लिए tailwindcss और @tailwindcss/typography, वह सर्च क्लाइंट, इंटीग्रेशन और एडेप्टर जिन्हें कॉन्फ़िग जोड़ता है, React जब कोई आइलैंड, उदाहरण, या असिस्टेंट उसका उपयोग करता हो, असिस्टेंट रूट के लिए ai, और EPUB एक्सपोर्ट के लिए epub-gen-memory — उन्हीं वर्शन रेंज पर जिनका उपयोग Blume स्वयं करता है। dev या build से पहले इंस्टॉल चलाएँ; इजेक्ट सूचीबद्ध करता है कि उसने क्या जोड़ा। इजेक्ट किए गए ऐप का हर पाथ रिलेटिव है, इसलिए वह किसी भी चेकआउट से बिल्ड होता है, CI सहित।
इसके बाद से, ऐप को उसकी अपनी स्क्रिप्ट्स (npm run dev, npm run build) के ज़रिए चलाएँ: इजेक्ट किए गए प्रोजेक्ट में blume dev, blume build, blume check, blume sync, और blume preview रुक जाते हैं और आपको उन्हीं स्क्रिप्ट्स की ओर इंगित करते हैं। blume eject को दोबारा चलाने पर वह भी इनकार कर देता है, क्योंकि इससे आपके संपादन ओवरराइट हो जाएँगे; फिर भी ऐप को फिर से जनरेट करने के लिए --force पास करें।
इजेक्ट क्या बनाए रखता है
छिपा हुआ रनटाइम जिन रूट्स को पेजों के रूप में सर्व करता है, वे ऐप के src/pages में लिखे जाते हैं, इसलिए ऐप उन्हीं URLs का उत्तर देता है: हर पेज का Markdown प्रतिरूप, अपने /404.md और /404.json प्रतिरूपों के साथ 404 पेज, होस्टेड MCP सर्वर, और /api/docs/… तथा /openapi.json पर उपलब्ध JSON डॉक्स API, जिसकी ओर llms.txt और नॉट-फ़ाउंड पेज एजेंट्स को इंगित करते हैं।
इजेक्ट किए गए ऐप की build स्क्रिप्ट सादा astro build चलाती है, और blume build जो आर्टिफ़ैक्ट्स ऊपर से जोड़ता है — सर्च इंडेक्स (और किसी होस्टेड प्रोवाइडर का इंडेक्स सिंक), llms.txt और llms-full.txt, sitemap.xml, robots.txt, agent-readability.json, .well-known डिस्कवरी फ़ाइलें, Agent Skills, और प्लेटफ़ॉर्म _redirects/_headers फ़ाइलें — वे अब भी उत्पन्न होते हैं: इजेक्ट किए गए astro.config.mjs में मौजूद Blume इंटीग्रेशन उन्हें Astro के astro:build:done हुक से लिखता है, और प्रोजेक्ट (आपका blume.config.ts और कंटेंट) को उसी तरह स्कैन करता है जैसे CLI करता था। इजेक्ट किया गया बिल्ड जो नहीं करता, वह है CLI का एडेप्टर पोस्ट-प्रोसेसिंग: Vercel और Cloudflare के Accept: text/markdown रूटिंग स्प्लाइस, Vercel फ़ंक्शन-बंडल ऑडिट, node() सर्वर-एंट्री रैपर (.well-known डिस्कवरी फ़ाइलों के मीडिया टाइप्स और CORS हेडर्स, डाउनलोड किए गए SVG पर सैंडबॉक्स, और हर रीडायरेक्ट का सटीक स्टेटस), वे हेडर नियम जिन्हें कोई netlify() सर्वर बिल्ड .netlify/v1/config.json में लिखता है, Cloudflare का Worker नामकरण (आपके प्रोजेक्ट के नाम पर) और प्रोजेक्ट रूट से wrangler deploy चलाने के लिए उसका .wrangler/deploy रीडायरेक्ट, और --analyze/--budget-* गेट।