הטמעה באתרים אחרים
הוספת אפליקציה לאתר אחר עם תגית סקריפט אחת: text build --embed, <text-console>, נתיבים מרונדרים בצד השרת ואחסון.
text build --embed בונה את האפליקציה כך שאתר אחר יוכל להוסיף אותה באמצעות תגית סקריפט אחת, למשל עמוד שיווקי, אתר תיעוד, עמוד WordPress או Webflow, או אפליקציית React 18. מבקרים שפותחים את DevTools מקבלים את האפליקציה במסוף. מבקרים אחרים מורידים את embed.js (כ-3 kB עם gzip) ושום דבר נוסף, אלא אם יש בעמוד <text-console>.
הטמעות הן ניסיוניות. text build --embed, האפשרות embed, הרכיב <text-console> והייצואים של @textjs/app/experimental עשויים להשתנות בגרסת משנה. ראו שינויים שוברי תאימות.
הוספת אפליקציה לאתר
לבניית האפליקציה:
text build --embedיש להעלות את dist/ לכל שירות אירוח סטטי או CDN, או להעתיק אותה לתיקייה public/ של האתר, ואז להוסיף תגית אחת לעמודי האתר:
<script type="module" src="https://console.example.com/embed.js"></script>embed.js טוען את הקבצים האחרים שלו ביחס לכתובת שלו, כך ש-dist/ עובדת מכל נתיב או מקור. היא מכילה רק את embed.js ואת assets/, ללא index.html או 404.html שעלולים להחליף את אלה של האתר עצמו. אפליקציה עם נתיבים מרונדרים בצד השרת, מטפלי נתיב או middleware זקוקה לשרת במקום העלאה סטטית רגילה. המתאמים vercel ו-cloudflare מריצים מטפלי נתיב ו-middleware, ונתיבים מרונדרים בצד השרת דורשים text start או את המתאם bun.
כדי להראות למבקרים היכן האפליקציה נמצאת, יש להוסיף <text-console> בכל מקום בעמוד. גם כל אלמנט עם data-textjs-engage מפעיל את האפליקציה בלחיצה:
<button type="button" data-textjs-engage>Open in the console</button>כדי לנסות זאת מקומית, יש להריץ text dev --embed. הפקודה מגישה את /embed.js עם פריים ראשון חדש בכל בקשה, ועמוד תצוגה מקדימה ב-/ (ב-/__textjs/embed עם standalone) שטוען אותו כפי שאתר אחר היה טוען אותו. גם אתר בפורט מקומי אחר יכול לטעון את http://localhost:3000/embed.js.
הגדרות
text build --embed שקול ל-embed: true בקובץ text.config.ts. כדי לשנות את ברירות המחדל, יש להעביר אובייקט:
import { defineConfig } from "@textjs/app";export default defineConfig({ embed: { initialPath: "/status", },});| אפשרות | ברירת מחדל | תיאור |
|---|---|---|
initialPath | "/" | הנתיב שבו המסוף נפתח. חייב להתחיל ב-/ |
globalName | "text" | השם הגלובלי שתחתיו האפליקציה מתקינה את ה-API שלה למסוף כשהיא מתחילה לפעול. false מונע התקנה של שם גלובלי |
initialClear | false | ניקוי מסוף העמוד לפני הפריים הראשון. כברירת מחדל, מה שהעמוד רשם קודם נשאר מעל האפליקציה |
standalone | false | בנייה גם של עמודי האפליקציה עצמה (index.html), כפי שעושה text build ללא embed |
allowOrigins | [] | אתרים שהעמודים שלהם רשאים להריץ נתיבים מרונדרים בצד השרת משרת זה. ראו נתיבים מרונדרים בצד השרת |
credentials | false | שליחת פרטי הזדהות של הדפדפן לשרת זה עם בקשות סשן חי מ-allowOrigins, במקום הסרת קובצי Cookie ו-Authorization. נדרשים מקורות מדויקים |
viewport.width × viewport.height (ברירת מחדל 120 × 36) הוא גודל הפריים הראשון. title, hints ו-action הם ברירות המחדל של <text-console>. אין ב-embed.js פריים ראשון מרונדר מראש כאשר מוגדר ppr: false, או כאשר middleware.ts עשוי להתאים ל-initialPath, משום שכל אחד יכול להוריד את embed.js. תצוגת מראה נשארת ריקה עד שהאפליקציה מציירת.
אופן הפעולה
- שום דבר נוסף לא נטען עד להבעת כוונה להשתמש במסוף.
embed.jsרושם למסוף את הפריים הראשון שרונדר מראש, ואז ממתין. האפליקציה נטענת כאשר DevTools מעוגן (בטעינה או מאוחר יותר), כאשר המבקר לוחץ בעמוד על קיצור מקשים של DevTools (F12, Ctrl+Shift+J, I, C או K, או ⌘⌥J, I, C או K ב-Mac), או כאשר הוא לוחץ על אלמנט<text-console>או[data-textjs-engage]. לאחר מכן, טעינות חוזרות באותה כרטיסייה מפעילות אותה מיד. DevTools בחלון משלו אינו משנה את גודל העמוד, ולכן מבקרים שפתחו אותו מתפריט רואים את הפריים הראשון עד שילחצו על אחד מהאלמנטים האלה. - במסכי מגע, האפליקציה אינה מופעלת. במכשיר שתואם ל-
(hover: none) and (pointer: coarse),embed.jsאינו רושם דבר למסוף ואינו מוסיף מאזינים. הקובץ היחיד שהוא טוען הוא זה של<text-console>, לצורך תצוגת מראה. - הקשות מגיעות לאפליקציה רק לאחר שהמבקר מפעיל את האינטראקציה איתה. לשם כך, יש ללחוץ על אלמנט
<text-console>או[data-textjs-engage], או ללחוץ על העמוד (לא על קישור, כפתור או שדה) כדי להחזיר את המיקוד מ-DevTools. הקלדה בשדות של העמוד עצמו או באלמנט[data-textjs-bypass]לעולם אינה מגיעה לאפליקציה, והגלגלת תמיד גוללת את העמוד. - כתובת העמוד, ההיסטוריה והכותרת שלו לעולם אינן משתנות. נתיבים,
<Link>ו-useRouter()מנווטים בתוך המסוף. באפליקציות שמרונדרות בצד הלקוח, כתובות מוחלטות נפתחות בכרטיסייה חדשה. - לאפליקציית 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 | כותרת הפס (ברירת מחדל: title בקובץ text.config.ts) |
hints | המקשים להצגה, בתבנית "← →:switch region, Enter:open" (ברירת מחדל: hints בקובץ text.config.ts) |
action | הפעולה הראשונה לביצוע, שמוצגת בתור "ללחוץ כאן ו…" (ברירת מחדל: action בקובץ text.config.ts, או לחיצה על המקש שברמז הראשון) |
במסכי מגע, תצוגת מראה מציגה את הפריים הראשון בלי להפעיל את האפליקציה, והפס מוסתר. הטקסט של האלמנט הוא באנגלית.
שמירת מקום לאלמנט
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. תצוגת מראה שומרת על יחס הממדים של הרשת, עד לרוחבה הטבעי (885 px ב-120 עמודות), ו-chrome מוסיף 43 px. הגודל אינו תלוי בגופנים. reserveCss(cols, rows) מתוך @textjs/app/experimental מחזירה את אותה שורה.
תצוגת מראה בחלק הגלוי הראשוני היא בדרך כלל האלמנט הגדול ביותר בעמוד, ולכן היא קובעת את מדד 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 מסיר את קובצי ה-Cookie ואת הכותרת
Authorizationשל סשנים מאתרים אחרים, כך ש-middleware רואה מבקר שאינו מחובר.credentials: trueשומר אותם, ודורש מקורות מדויקים. הדפדפן שולח אותם כשהעמוד והשרת חולקים דומיין שניתן לרישום (www.example.comו-console.example.com) וקובץ ה-Cookie חל על השרת (Domain=example.com, או שהוגדר ב-console.example.com). מאתרים אחרים הוא שולח רק קובצי Cookie שהוגדרו עםSameSite=None; Secure, ודפדפנים שחוסמים קובצי Cookie של צד שלישי אינם שולחים אף אחד מהם. - סשן חי שולח בקשות קדם
OPTIONSאל/__textjs/liveמיד עם ההתחברות, ובכל התחברות מחדש. בקשות קדם אינן נושאות קובצי Cookie אוAuthorization, ולכן middleware שתואם לנתיב הזה חייב לאפשר לבקשותOPTIONSלעבור ללא כניסה לחשבון. - מאחורי פרוקסי הפוך או CDN, יש להגדיר
server.trustProxy(ראו מאחורי פרוקסי). בלעדיו, לכל מבקר יש את כתובת הפרוקסי, ולכן כולם חולקים מגבלת סשנים אחת ללקוח.
text dev מקבל עמודים ב-localhost, 127.0.0.1 ו-[::1] בכל פורט ללא allowOrigins, אלא אם הוגדר credentials.
אירוח
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 אינו זקוק לסקריפטים או לסגנונות מוטבעים. יש להוסיף את הערכים הבאים למדיניות של הדף המארח, עם 'self' במקום המקור כאשר dist/ נמצא באותו מקור:
| הנחיה | ערך | שימוש |
|---|---|---|
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) | החזרת פונקציית mount(element, shell?) עבור <text-console> |
reserveCss(cols, rows) | ה-CSS ששומר מקום ל-<text-console> |
ENGAGE_SELECTOR ו-BYPASS_SELECTOR הם הבוררים של אלמנטים שמפעילים את האפליקציה ושל אלמנטים ששומרים את קלט המקשים שלהם לעצמם.