दूसरी साइटों में एम्बेड करना
एक स्क्रिप्ट टैग से दूसरी साइट में ऐप जोड़ें: text build --embed, <text-console>, सर्वर पर रेंडर किए गए रूट और होस्टिंग।
text build --embed आपका ऐप इस तरह बिल्ड करता है कि दूसरी साइट उसे एक स्क्रिप्ट टैग से जोड़ सके, जैसे मार्केटिंग पेज, डॉक्यूमेंटेशन साइट, WordPress या Webflow पेज, या React 18 ऐप। DevTools खोलने वाले विज़िटर को आपका ऐप कंसोल में मिलता है। बाकी विज़िटर सिर्फ़ embed.js (gzip के साथ करीब 3 kB) डाउनलोड करते हैं, जब तक पेज पर <text-console> न हो।
एम्बेड अभी प्रयोगात्मक हैं। text build --embed, embed विकल्प, <text-console> और @textjs/app/experimental के एक्सपोर्ट माइनर रिलीज़ में बदल सकते हैं। ब्रेकिंग बदलाव देखें।
साइट पर ऐप जोड़ना
ऐप बिल्ड करें:
text build --embeddist/ को किसी स्टैटिक होस्ट या CDN पर अपलोड करें, या साइट के public/ फ़ोल्डर में कॉपी करें, फिर साइट के पेजों में एक टैग जोड़ें:
<script type="module" src="https://console.example.com/embed.js"></script>embed.js अपनी दूसरी फ़ाइलें अपने URL के सापेक्ष लोड करता है, इसलिए dist/ किसी भी पाथ या ओरिजिन से काम करता है। इसमें सिर्फ़ embed.js और assets/ होते हैं, कोई index.html या 404.html नहीं जो साइट की अपनी फ़ाइलों की जगह ले सके। सर्वर पर रेंडर किए गए रूट, रूट हैंडलर या मिडलवेयर वाले ऐप को साधारण स्टैटिक अपलोड के बजाय सर्वर चाहिए। vercel और cloudflare अडैप्टर रूट हैंडलर और मिडलवेयर चलाते हैं, और सर्वर पर रेंडर किए गए रूट को text start या bun अडैप्टर चाहिए।
विज़िटर को ऐप की जगह दिखाने के लिए, पेज पर कहीं भी <text-console> जोड़ें। data-textjs-engage वाला कोई भी एलिमेंट दबाने पर भी ऐप शुरू होता है:
<button type="button" data-textjs-engage>Open in the console</button>इसे लोकल पर आज़माने के लिए, text dev --embed चलाएं। यह हर रिक्वेस्ट पर नए पहले फ़्रेम के साथ /embed.js सर्व करता है, और / पर (standalone के साथ /__textjs/embed पर) एक प्रीव्यू पेज सर्व करता है जो इसे दूसरी साइट की तरह लोड करता है। दूसरे लोकल पोर्ट की साइट भी http://localhost:3000/embed.js लोड कर सकती है।
कॉन्फ़िगरेशन
text build --embed का असर text.config.ts फ़ाइल में embed: true जैसा ही है। डिफ़ॉल्ट बदलने के लिए ऑब्जेक्ट पास करें:
import { defineConfig } from "@textjs/app";export default defineConfig({ embed: { initialPath: "/status", },});| विकल्प | डिफ़ॉल्ट | जानकारी |
|---|---|---|
initialPath | "/" | वह रूट जहां कंसोल खुलता है। / से शुरू होना चाहिए |
globalName | "text" | वह ग्लोबल जिसके तहत ऐप शुरू होने पर अपना कंसोल API इंस्टॉल करता है। false कुछ इंस्टॉल नहीं करता |
initialClear | false | पहले फ़्रेम से पहले पेज का कंसोल साफ़ करें। डिफ़ॉल्ट रूप से, पेज के पहले के लॉग ऐप के ऊपर रहते हैं |
standalone | false | ऐप के अपने पेज (index.html) भी बिल्ड करें, जैसे embed के बिना text build करता है |
allowOrigins | [] | वे साइटें जिनके पेज इस सर्वर से सर्वर पर रेंडर किए गए रूट चला सकते हैं। सर्वर पर रेंडर किए गए रूट देखें |
credentials | false | कुकी और Authorization हटाने के बजाय, allowOrigins से आने वाली लाइव सेशन रिक्वेस्ट के साथ इस सर्वर के ब्राउज़र क्रेडेंशियल भेजें। सटीक ओरिजिन ज़रूरी हैं |
viewport.width × viewport.height (डिफ़ॉल्ट 120 × 36) पहले फ़्रेम का आकार है। title, hints और action, <text-console> के डिफ़ॉल्ट हैं। ppr: false होने पर, या जब middleware.ts का initialPath से मिलान हो सकता है, embed.js में प्रीरेंडर किया गया पहला फ़्रेम नहीं होता, क्योंकि कोई भी embed.js डाउनलोड कर सकता है। ऐप के पेंट होने तक मिरर खाली रहता है।
इसका व्यवहार
- कंसोल इंटेंट तक और कुछ लोड नहीं होता।
embed.jsप्रीरेंडर किया गया पहला फ़्रेम लॉग करता है, फिर इंतज़ार करता है। ऐप तब लोड होता है जब DevTools डॉक हो (लोड के समय या बाद में), जब विज़िटर पेज पर DevTools शॉर्टकट दबाए (F12, Ctrl+Shift+J, I, C या K, या Mac पर ⌘⌥J, I, C या K), या जब वह<text-console>या[data-textjs-engage]एलिमेंट दबाए। उसके बाद, उसी टैब में रीलोड करने पर यह तुरंत शुरू होता है। अपनी अलग विंडो में DevTools पेज का आकार नहीं बदलता, इसलिए मेन्यू से इसे खोलने वाले विज़िटर को उन एलिमेंट में से कोई एक दबाने तक पहला फ़्रेम दिखता है। - टच स्क्रीन पर ऐप शुरू नहीं होता।
(hover: none) and (pointer: coarse)से मेल खाने वाले डिवाइस परembed.jsकुछ लॉग नहीं करता और कोई लिसनर नहीं जोड़ता। यह सिर्फ़ मिरर के लिए<text-console>की फ़ाइल लोड करता है। - विज़िटर के ऐप को सक्रिय करने के बाद ही कुंजियां ऐप तक पहुंचती हैं। इसे सक्रिय करने के लिए, वे
<text-console>या[data-textjs-engage]एलिमेंट दबाते हैं, या DevTools से फ़ोकस वापस लाने के लिए पेज पर क्लिक करते हैं (लिंक, बटन या फ़ील्ड पर नहीं)। पेज की अपनी फ़ील्ड या[data-textjs-bypass]एलिमेंट में टाइप किया गया इनपुट कभी ऐप तक नहीं पहुंचता, और व्हील हमेशा पेज को स्क्रॉल करता है। - पेज का URL, हिस्ट्री और टाइटल कभी नहीं बदलते। रूट,
<Link>औरuseRouter()कंसोल के अंदर नेविगेट करते हैं। क्लाइंट पर रेंडर किए गए ऐप में, ऐब्सलूट URL नए टैब में खुलते हैं। - पेज पर मौजूद Text.js ऐप को प्राथमिकता मिलती है। उसके शुरू होने पर, चल रहा एम्बेड अनमाउंट हो जाता है, कुंजियां पढ़ना बंद करता है और अपना ग्लोबल हटा देता है। जब किसी दूसरे ऐप के पास कंसोल हो, तब लोड होने वाला एम्बेड एक चेतावनी लॉग करता है और शुरू नहीं होता।
- एम्बेड अपना React 19 बंडल करता है। इसमें
react-domया बेयर इंपोर्ट नहीं हैं, इसलिए पेज का React वर्शन,reactका इंपोर्ट मैप और React DevTools इसे प्रभावित नहीं करते।
क्या कब लोड होता है
| फ़ाइल | लोड होने का समय | gzip |
|---|---|---|
embed.js | पेज के साथ | 2.9 kB |
assets/text-console-[hash].js | जब पेज पर <text-console> हो | 4.0 kB |
assets/embed-client-[hash].js और assets/yoga-[hash].wasm | कंसोल इंटेंट पर | 112.8 kB + 29.0 kB |
assets/embed-player-[hash].js | सर्वर पर रेंडर किए गए रूट के लिए, कंसोल इंटेंट पर क्लाइंट की जगह | 22.7 kB |
assets/bidi-[hash].js | जब ऐप पहली बार दाएं से बाएं टेक्स्ट दिखाए (bidi इंजन लोड करना देखें) | 10.1 kB |
ये आकार 120 × 36 के बॉर्डर वाले स्टेटस बॉक्स के लिए हैं। text build --embed आपके ऐप के आकार प्रिंट करता है। assets/embed-[hash].js, embed.js की एक कॉपी है जिसका नाम कभी नहीं बदलता (डिप्लॉय देखें)।
<text-console>
embed.js, <text-console> को परिभाषित करता है और उसकी फ़ाइल तभी लोड करता है जब पेज पर वह मौजूद हो। यह Shadow DOM इस्तेमाल करता है, इसलिए पेज का CSS अंदर नहीं पहुंचता, और style-src 'self' पॉलिसी में बदलाव की ज़रूरत नहीं होती।
<text-console></text-console><text-console mirror></text-console><text-console chrome></text-console>ऐट्रिब्यूट के बिना यह एक लाइन का बार है। यह कंसोल खोलने के लिए ब्राउज़र का शॉर्टकट दिखाता है, फिर क्लिक करने को कहता है, फिर ऐप की कुंजियों की सूची दिखाता है, जो ऐप तक पहुंचने पर चमकती हैं। Safari में, पहला चरण Safari की सेटिंग में “वेब डेवलपर के लिए सुविधाएं दिखाएं” चालू करने को कहता है।
mirror के साथ, यह पेज पर ऐप का फ़्रेम दिखाता है: कुछ और लोड होने से पहले प्रीरेंडर किया गया पहला फ़्रेम, फिर चलता हुआ ऐप। इसे दबाने पर ऐप शुरू होता है और विज़िटर की कुंजियां उस तक पहुंचती हैं, इसलिए DevTools बंद होने पर भी ऐप काम करता है। chrome मिरर के चारों ओर DevTools विंडो बनाता है और mirror को भी लागू करता है।
| ऐट्रिब्यूट | जानकारी |
|---|---|
mirror | ऐप के फ़्रेम दिखाएं |
chrome | DevTools विंडो में फ़्रेम दिखाएं |
title | बार का टाइटल (डिफ़ॉल्ट: text.config.ts फ़ाइल में title) |
hints | सूची में दिखाने वाली कुंजियां, जैसे "← →:switch region, Enter:open" (डिफ़ॉल्ट: text.config.ts फ़ाइल में hints) |
action | पहले क्या करना है, जिसे “यहां क्लिक करें और …” के रूप में दिखाया जाता है (डिफ़ॉल्ट: text.config.ts फ़ाइल में action, या पहला हिंट दबाएं) |
टच स्क्रीन पर, मिरर ऐप शुरू किए बिना पहला फ़्रेम दिखाता है, और बार छिपा रहता है। एलिमेंट का टेक्स्ट अंग्रेज़ी में है।
इसके लिए जगह रखना
text build --embed आपके ऐप के ग्रिड के लिए CSS की एक लाइन प्रिंट करता है। इसे पेज की स्टाइलशीट में पेस्ट करें। यह एलिमेंट की फ़ाइल लोड होने से पहले <text-console> का आकार तय करता है, ताकि फ़ाइल आने पर पेज पर कुछ न खिसके। डिफ़ॉल्ट 120 × 36 ग्रिड के लिए लाइन यह है:
text-console{display:block;min-height:58px}text-console:is([mirror],[chrome]){position:relative;box-sizing:content-box;min-height:0;max-width:885px;aspect-ratio:885/522}text-console[chrome]{padding-top:43px}@media (hover:none) and (pointer:coarse){text-console:not([mirror],[chrome]){display:none}}बार की ऊंचाई 58 px है। मिरर अपनी स्वाभाविक चौड़ाई (120 कॉलम पर 885 px) तक ग्रिड का आस्पेक्ट रेशियो बनाए रखता है, और chrome 43 px जोड़ता है। आकार फ़ॉन्ट पर निर्भर नहीं करता। @textjs/app/experimental का reserveCss(cols, rows) वही लाइन लौटाता है।
बिना स्क्रॉल किए दिखने वाला मिरर आम तौर पर पेज का सबसे बड़ा एलिमेंट होता है, इसलिए वही पेज का Largest Contentful Paint (LCP) तय करता है। इसका पहला फ़्रेम embed.js और फिर एलिमेंट की फ़ाइल के क्रम से लोड होने के बाद बनता है। बाकी पेज वैसे ही पेंट होता है जैसे एम्बेड के बिना होता। पेज में और नीचे का मिरर LCP को प्रभावित नहीं करता। अपनी डिफ़र्ड स्क्रिप्ट वाले पेज, जैसे React ऐप, पर <script type="module" async src="…"> उन स्क्रिप्ट का इंतज़ार किए बिना embed.js चलाता है, इसलिए बिना स्क्रॉल किए दिखने वाला मिरर जल्दी बनता है।
स्टाइलिंग
मिरर को बीच में रखने के लिए, उसे ऑटो इनलाइन मार्जिन दें:
text-console { margin-inline: auto;}यह डिफ़ॉल्ट रूप से डार्क है। हल्के रंगों के लिए color-scheme: light सेट करें, या वे दो रंग सेट करें जिनके मिश्रण से बाकी सभी शेड बनते हैं। ::part(frame) फ़्रेम को स्टाइल करता है। chrome DevTools का अपना बैकग्राउंड बनाए रखता है।
text-console { --text-console-bg: #fafafa; --text-console-fg: #171717;}text-console::part(frame) { border-radius: 0;}सर्वर पर रेंडर किए गए रूट
जब initialPath ऐसा रूट हो जिसमें export const runtime = "server" है, तो embed.js क्लाइंट रनटाइम के बजाय फ़्रेम प्लेयर लोड करता है। यह उसी सर्वर से फ़्रेम स्ट्रीम करता है जिससे embed.js आया था, इसलिए text start या bun अडैप्टर चलाएं। दूसरे अडैप्टर बिल्ड को विफल कर देते हैं। सर्वर रेंडरिंग देखें।
दूसरी साइट का पेज लाइव सेशन तभी खोल सकता है जब उसका ओरिजिन allowOrigins में हो:
import { defineConfig } from "@textjs/app";export default defineConfig({ embed: { initialPath: "/status", allowOrigins: ["https://www.example.com", "https://*.example.com"], },});- सेशन सिर्फ़ उस ओरिजिन को जवाब देता है जिसने उसे खोला था।
- डिफ़ॉल्ट रूप से, Text.js दूसरी साइटों के सेशन की कुकी और
Authorizationहेडर हटा देता है, इसलिए मिडलवेयर को साइन आउट किया हुआ विज़िटर दिखता है।credentials: trueइन्हें बनाए रखता है, और इसके लिए सटीक ओरिजिन ज़रूरी हैं। ब्राउज़र इन्हें तब भेजता है जब पेज और सर्वर का रजिस्टर करने योग्य डोमेन एक हो (www.example.comऔरconsole.example.com) और कुकी सर्वर को कवर करती हो (Domain=example.com, याconsole.example.comपर सेट हो)। दूसरी साइटों से यह सिर्फ़SameSite=None; Secureके साथ सेट की गई कुकी भेजता है, और थर्ड-पार्टी कुकी ब्लॉक करने वाले ब्राउज़र कोई कुकी नहीं भेजते। - लाइव सेशन कनेक्ट होते ही और दोबारा कनेक्ट होने पर
/__textjs/liveपरOPTIONSप्रीफ़्लाइट भेजता है। प्रीफ़्लाइट में कुकी याAuthorizationनहीं होते, इसलिए उस पाथ से मेल खाने वाले मिडलवेयर को बिना साइन इन केOPTIONSरिक्वेस्ट आगे जाने देनी चाहिए। - रिवर्स प्रॉक्सी या CDN के पीछे,
server.trustProxyसेट करें (प्रॉक्सी के पीछे देखें)। इसके बिना, हर विज़िटर का पता प्रॉक्सी का पता होता है, इसलिए वे सभी एक ही प्रति-क्लाइंट सेशन सीमा साझा करते हैं।
जब तक credentials सेट न हो, text dev किसी भी पोर्ट पर localhost, 127.0.0.1 और [::1] के पेज allowOrigins के बिना स्वीकार करता है।
होस्टिंग
text start और bun, vercel व cloudflare अडैप्टर इन हेडर के साथ एम्बेड बिल्ड सर्व करते हैं। static अडैप्टर इन्हें _headers में लिखता है, जिसे Cloudflare Pages और Netlify पढ़ते हैं। किसी दूसरे होस्ट पर, जैसे स्टोरेज बकेट या साइट के अपने public/ फ़ोल्डर में, इन्हें खुद सेट करें:
| पाथ | हेडर |
|---|---|
/embed.js | Cache-Control: public, max-age=0, must-revalidate |
/assets/* | Cache-Control: public, max-age=31536000, immutable |
| दोनों, जब साइट दूसरे ओरिजिन पर हो | Access-Control-Allow-Origin: * और Cross-Origin-Resource-Policy: cross-origin |
ब्राउज़र मॉड्यूल स्क्रिप्ट और WebAssembly को CORS के साथ फ़ेच करते हैं, इसलिए दूसरे ओरिजिन की फ़ाइलों को Access-Control-Allow-Origin चाहिए। yoga-[hash].wasm को कंप्रेस करके application/wasm के रूप में सर्व करें (WebAssembly देखें)।
डिप्लॉय
हर पेज लोड पर embed.js फिर से वैलिडेट होता है, इसलिए नए विज़िटर को नया डिप्लॉय तुरंत मिलता है। जिस टैब ने पिछला embed.js लोड किया था, वह विज़िटर के कंसोल खोलने पर पिछले बिल्ड की फ़ाइलें ही इंपोर्ट करता है, भले ही कई घंटे बीत चुके हों। पिछले डिप्लॉय का assets/ ऑनलाइन रखें: हटाकर सिंक करने के बजाय पुरानी फ़ाइलों के ऊपर नई फ़ाइलें कॉपी करें, और एक या दो दिन बाद पुरानी फ़ाइलें हटाएं। किसी बिल्ड को पिन करने के लिए, टैग में उसका assets/embed-[hash].js रखें। उस बिल्ड की फ़ाइलें भी ऑनलाइन रहनी चाहिए।
फ़ाइलें उपलब्ध न होने पर, embed.js एक चेतावनी लॉग करता है जो विज़िटर से रीलोड करने को कहती है, और ख़ुद ऐप शुरू या पेज रीलोड नहीं करता।
कॉन्टेंट सिक्योरिटी पॉलिसी
embed.js को इनलाइन स्क्रिप्ट या स्टाइल की ज़रूरत नहीं है। होस्ट पेज की पॉलिसी में ये जोड़ें, और जब dist/ उसी ओरिजिन पर हो तो ओरिजिन की जगह 'self' रखें:
| डायरेक्टिव | वैल्यू | किसके लिए |
|---|---|---|
script-src | https://console.example.com | embed.js और उसके चंक |
script-src | 'wasm-unsafe-eval' | क्लाइंट पर रेंडर किए गए ऐप में Yoga |
connect-src | https://console.example.com | yoga-[hash].wasm, और सर्वर पर रेंडर किए गए रूट के लिए /__textjs/live |
img-src | data: | सिर्फ़ वह <text-console mirror> जिसका ऐप <Image> या <Pixels> ड्रॉ करता है |
ऐप ख़ुद लोड करना
जो पेज अपने तरीके से ऐप लोड करते हैं, उनके लिए @textjs/app/experimental वे हिस्से एक्सपोर्ट करता है जिनसे embed.js बना है:
| एक्सपोर्ट | विवरण |
|---|---|
startEmbed(paint, boot, src, element?) | जो embed.js चलाता है: पहला फ़्रेम लॉग करें, कंसोल इस्तेमाल करने के संकेत का इंतज़ार करें, फिर boot कॉल करें |
bootEmbedClient(options) | इन-मेमोरी रूटिंग और पेज के लिए नियंत्रित कीबोर्ड इनपुट के साथ क्लाइंट पर रेंडर किया गया ऐप शुरू करें |
bootEmbedPlayer(options) | सर्वर पर रेंडर किए गए रूट के लिए फ़्रेम प्लेयर शुरू करें |
createHostInput(options) | ऊपर दिए गए नियमों का पालन करने वाला कीबोर्ड इनपुट |
createTextConsoleMount(config) | <text-console> के लिए mount(element, shell?) फ़ंक्शन लौटाता है |
reserveCss(cols, rows) | <text-console> की जगह रिज़र्व करने वाली CSS |
ENGAGE_SELECTOR और BYPASS_SELECTOR उन एलिमेंट के सेलेक्टर हैं जो ऐप शुरू करते हैं और उन एलिमेंट के जो अपनी कुंजियां अपने पास रखते हैं।