Intégration dans d’autres sites

Ajoutez une application à un autre site avec une balise script : text build --embed, <text-console>, routes avec rendu côté serveur et hébergement.

text build --embed compile votre application pour qu’un autre site puisse l’ajouter avec une seule balise script, par exemple une page marketing, un site de documentation, une page WordPress ou Webflow, ou une application React 18. Les visiteurs qui ouvrent DevTools retrouvent votre application dans la console. Les autres visiteurs téléchargent embed.js (environ 3 ko avec gzip) et rien d’autre, sauf si la page contient un <text-console>.

Les intégrations sont expérimentales. text build --embed, l’option embed, <text-console> et les exports de @textjs/app/experimental peuvent changer dans une version mineure. Consultez Changements incompatibles.

Ajouter une application à un site

Compilez l’application :

text build --embed

Téléversez dist/ sur un hébergement statique ou un CDN, ou copiez-le dans le dossier public/ du site, puis ajoutez une balise aux pages du site :

<script type="module" src="https://console.example.com/embed.js"></script>

embed.js charge ses autres fichiers relativement à sa propre URL, donc dist/ fonctionne depuis n’importe quel chemin ou origine. Il contient uniquement embed.js et assets/, sans index.html ni 404.html qui pourraient remplacer ceux du site. Une application avec des routes avec rendu côté serveur, des gestionnaires de route ou des middlewares nécessite un serveur plutôt qu’un simple téléversement statique. Les adaptateurs vercel et cloudflare exécutent les gestionnaires de route et les middlewares, et les routes avec rendu côté serveur nécessitent text start ou l’adaptateur bun.

Pour montrer aux visiteurs où se trouve l’application, ajoutez <text-console> n’importe où dans la page. Tout élément avec data-textjs-engage démarre aussi l’application lorsqu’on appuie dessus :

<button type="button" data-textjs-engage>Open in the console</button>

Pour l’essayer en local, exécutez text dev --embed. Il sert /embed.js avec une nouvelle première frame à chaque requête, et une page d’aperçu à / (/__textjs/embed avec standalone) qui le charge comme le ferait un autre site. Un site sur un autre port local peut aussi charger http://localhost:3000/embed.js.

Configuration

text build --embed équivaut à embed: true dans text.config.ts. Passez un objet pour modifier les valeurs par défaut :

text.config.tsts
import { defineConfig } from "@textjs/app";export default defineConfig({  embed: {    initialPath: "/status",  },});
OptionValeur par défautDescription
initialPath"/"La route sur laquelle la console s’ouvre. Doit commencer par /
globalName"text"La variable globale sous laquelle l’application installe son API de console au démarrage. false n’en installe aucune
initialClearfalseEffacer la console de la page avant la première frame. Par défaut, les messages précédents de la page restent au-dessus de l’application
standalonefalseCompiler aussi les propres pages de l’application (index.html), comme le fait text build sans embed
allowOrigins[]Sites dont les pages peuvent exécuter des routes avec rendu côté serveur depuis ce serveur. Consultez Routes avec rendu côté serveur
credentialsfalseEnvoyer les informations d’authentification du navigateur pour ce serveur avec les requêtes de session en direct provenant de allowOrigins, au lieu de supprimer les cookies et Authorization. Nécessite des origines exactes

viewport.width × viewport.height (120 × 36 par défaut) définit la taille de la première frame. title, hints et action sont les valeurs par défaut de <text-console>. embed.js ne contient pas de première frame prérendue avec ppr: false, ou lorsque middleware.ts pourrait correspondre à initialPath, car tout le monde peut télécharger embed.js. Un miroir reste vide jusqu’à ce que l’application effectue son rendu.

Comportement

  • Rien d’autre ne se charge avant une intention d’utiliser la console. embed.js affiche la première frame prérendue dans la console, puis attend. L’application se charge lorsque DevTools est ancré (au chargement ou plus tard), lorsque le visiteur utilise un raccourci DevTools sur la page (F12, Ctrl+Shift+J, I, C ou K, ou ⌘⌥J, I, C ou K sur Mac), ou lorsqu’il appuie sur un <text-console> ou un élément [data-textjs-engage]. Ensuite, les rechargements dans le même onglet la démarrent immédiatement. DevTools dans sa propre fenêtre ne modifie pas la taille de la page : les visiteurs qui l’ont ouvert depuis un menu voient donc la première frame jusqu’à ce qu’ils appuient sur l’un de ces éléments.
  • Sur les écrans tactiles, l’application ne démarre pas. Sur un appareil correspondant à (hover: none) and (pointer: coarse), embed.js n’affiche rien dans la console et n’ajoute aucun écouteur. Le seul fichier qu’il charge est celui de <text-console>, pour un miroir.
  • Les touches ne parviennent à l’application qu’après son activation par le visiteur. Pour l’activer, il appuie sur un <text-console> ou un élément [data-textjs-engage], ou clique sur la page (pas sur un lien, un bouton ou un champ) pour reprendre le focus à DevTools. La saisie dans les champs de la page ou dans un élément [data-textjs-bypass] ne parvient jamais à l’application, et la molette fait toujours défiler la page.
  • L’URL, l’historique et le titre de la page ne changent jamais. Les routes, <Link> et useRouter() naviguent dans la console. Dans les applications avec rendu côté client, les URL absolues s’ouvrent dans un nouvel onglet.
  • Une application Text.js sur la page est prioritaire. Lorsqu’elle démarre, une intégration en cours d’exécution se démonte, cesse de lire les touches et supprime sa variable globale. Une intégration qui se charge alors qu’une autre application occupe la console affiche un avertissement et ne démarre pas.
  • L’intégration embarque sa propre version de React 19. Elle n’a ni react-dom ni imports nus, donc la version de React de la page, une table d’importation pour react et React DevTools n’ont aucun effet sur elle.

Chargement des fichiers

FichierChargementgzip
embed.jsAvec la page2,9 ko
assets/text-console-[hash].jsLorsque la page contient un <text-console>4,0 ko
assets/embed-client-[hash].js et assets/yoga-[hash].wasmÀ l’intention d’utiliser la console112,8 ko + 29,0 ko
assets/embed-player-[hash].jsÀ l’intention d’utiliser la console, à la place du client, pour une route avec rendu côté serveur22,7 ko
assets/bidi-[hash].jsLa première fois que l’application affiche du texte de droite à gauche (voir Chargement du moteur bidi)10,1 ko

Les tailles correspondent à un encadré d’état avec bordure de 120 × 36. text build --embed affiche celles de votre application. assets/embed-[hash].js est une copie de embed.js sous un nom qui ne change jamais (voir Déploiements).

<text-console>

embed.js définit <text-console> et ne charge son fichier que lorsqu’un élément est présent sur la page. Il utilise le Shadow DOM, donc le CSS de la page n’atteint pas son contenu, et une politique style-src 'self' ne nécessite aucune modification.

<text-console></text-console><text-console mirror></text-console><text-console chrome></text-console>

Sans attributs, c’est une barre d’une ligne. Elle affiche le raccourci du navigateur pour ouvrir la console, puis demande un clic, puis liste les touches de l’application, qui s’illuminent lorsqu’elles lui parviennent. Dans Safari, la première étape indique d’activer « Afficher les fonctionnalités pour les développeurs web » dans les réglages de Safari.

Avec mirror, l’élément affiche la frame de l’application sur la page : la première frame prérendue avant tout autre chargement, puis l’application en cours d’exécution. Appuyer dessus démarre l’application et lui transmet les touches du visiteur, donc l’application fonctionne avec DevTools fermé. chrome dessine une fenêtre DevTools autour du miroir et implique mirror.

AttributDescription
mirrorAfficher les frames de l’application
chromeAfficher les frames dans une fenêtre DevTools
titleLe titre de la barre (par défaut : title dans text.config.ts)
hintsLes touches à lister, sous la forme "← →:switch region, Enter:open" (par défaut : hints dans text.config.ts)
actionLa première action à effectuer, affichée sous la forme « Cliquez ici et… » (par défaut : action dans text.config.ts, ou appuyer sur la première touche indiquée)

Sur les écrans tactiles, un miroir affiche la première frame sans démarrer l’application, et la barre est masquée. Le texte de l’élément est en anglais.

Réserver son espace

text build --embed affiche une ligne de CSS pour la grille de votre application. Collez-la dans la feuille de style de la page. Elle dimensionne <text-console> avant le chargement du fichier de l’élément, donc rien sur la page ne bouge lorsque le fichier arrive. Pour la grille par défaut de 120 × 36, la ligne est :

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}}

La barre mesure 58 px de haut. Un miroir conserve les proportions de la grille, jusqu’à sa largeur naturelle (885 px pour 120 colonnes), et chrome ajoute 43 px. La taille ne dépend pas des polices. reserveCss(cols, rows) de @textjs/app/experimental renvoie la même ligne.

Un miroir visible sans défilement est généralement le plus grand élément de la page, il détermine donc le Largest Contentful Paint (LCP) de la page. Sa première frame s’affiche après le chargement de embed.js puis du fichier de l’élément, l’un après l’autre. Le reste de la page s’affiche comme il le ferait sans l’intégration. Un miroir plus bas dans la page n’affecte pas le LCP. Sur une page ayant ses propres scripts différés, comme une application React, <script type="module" async src="…"> exécute embed.js sans les attendre, donc un miroir visible sans défilement s’affiche plus tôt.

Mise en forme

Pour centrer un miroir, donnez-lui des marges en ligne automatiques :

text-console {  margin-inline: auto;}

Il est sombre par défaut. Définissez color-scheme: light pour des couleurs claires, ou définissez les deux couleurs dont le mélange produit toutes les autres nuances. ::part(frame) met en forme la frame elle-même. chrome conserve l’arrière-plan de DevTools.

text-console {  --text-console-bg: #fafafa;  --text-console-fg: #171717;}text-console::part(frame) {  border-radius: 0;}

Routes avec rendu côté serveur

Lorsque initialPath est une route avec export const runtime = "server", embed.js charge le lecteur de frames à la place de l’environnement d’exécution client. Il diffuse les frames depuis le serveur d’où provient embed.js : exécutez donc text start ou l’adaptateur bun. Les autres adaptateurs font échouer la compilation. Consultez Rendu côté serveur.

Une page sur un autre site ne peut ouvrir une session en direct que si son origine figure dans allowOrigins :

text.config.tsts
import { defineConfig } from "@textjs/app";export default defineConfig({  embed: {    initialPath: "/status",    allowOrigins: ["https://www.example.com", "https://*.example.com"],  },});
  • Une session ne répond qu’à l’origine qui l’a ouverte.
  • Par défaut, Text.js supprime les cookies et l’en-tête Authorization des sessions provenant d’autres sites, donc le middleware voit un visiteur déconnecté. credentials: true les conserve et nécessite des origines exactes. Le navigateur les envoie lorsque la page et le serveur partagent un domaine enregistrable (www.example.com et console.example.com) et que le cookie couvre le serveur (Domain=example.com, ou défini sur console.example.com). Depuis d’autres sites, il envoie uniquement les cookies définis avec SameSite=None; Secure, et les navigateurs qui bloquent les cookies tiers n’en envoient aucun.
  • Une session en direct envoie des requêtes préliminaires OPTIONS à /__textjs/live dès sa connexion et lors de ses reconnexions. Les requêtes préliminaires ne contiennent ni cookies ni Authorization, donc un middleware qui correspond à ce chemin doit laisser passer les requêtes OPTIONS sans authentification.
  • Derrière un proxy inverse ou un CDN, définissez server.trustProxy (voir Derrière un proxy). Sans cela, tous les visiteurs ont l’adresse du proxy, donc ils partagent tous une même limite de sessions par client.

text dev accepte les pages sur localhost, 127.0.0.1 et [::1] sur n’importe quel port sans allowOrigins, sauf si credentials est défini.

Hébergement

text start et les adaptateurs bun, vercel et cloudflare servent une compilation d’intégration avec ces en-têtes. L’adaptateur static les écrit dans _headers, que Cloudflare Pages et Netlify lisent. Sur tout autre hébergement, comme un bucket de stockage ou le dossier public/ du site, définissez-les vous-même :

CheminEn-têtes
/embed.jsCache-Control: public, max-age=0, must-revalidate
/assets/*Cache-Control: public, max-age=31536000, immutable
Les deux, lorsque le site est sur une autre origineAccess-Control-Allow-Origin: * et Cross-Origin-Resource-Policy: cross-origin

Les navigateurs récupèrent les scripts de module et WebAssembly avec CORS, donc les fichiers sur une autre origine nécessitent Access-Control-Allow-Origin. Servez yoga-[hash].wasm en tant que application/wasm, compressé (voir WebAssembly).

Déploiements

embed.js est revalidé à chaque chargement de page, donc les nouveaux visiteurs reçoivent immédiatement un nouveau déploiement. Un onglet qui a chargé le précédent embed.js importe toujours les fichiers du build précédent lorsque le visiteur ouvre la console, parfois des heures plus tard. Gardez le dossier assets/ du déploiement précédent en ligne : copiez les nouveaux fichiers par-dessus les anciens au lieu de synchroniser avec suppression, puis supprimez les anciens fichiers après un jour ou deux. Pour figer un build, placez son fichier assets/embed-[hash].js dans la balise. Les fichiers de ce build doivent aussi rester en ligne.

Lorsque les fichiers ne sont plus disponibles, embed.js affiche un avertissement qui demande au visiteur de recharger la page, sans démarrer l’application ni recharger la page lui-même.

Politique de sécurité du contenu

embed.js ne nécessite aucun script ni style en ligne. Ajoutez ces valeurs à la politique de la page hôte, avec 'self' à la place de l’origine lorsque dist/ est sur la même origine :

DirectiveValeurUsage
script-srchttps://console.example.comembed.js et ses fragments
script-src'wasm-unsafe-eval'Yoga, dans les applications rendues côté client
connect-srchttps://console.example.comyoga-[hash].wasm, et /__textjs/live pour les routes avec rendu côté serveur
img-srcdata:Uniquement un <text-console mirror> dont l’application dessine <Image> ou <Pixels>

Charger l’application vous-même

@textjs/app/experimental exporte les éléments qui composent embed.js, pour les pages qui chargent une application à leur manière :

ExportDescription
startEmbed(paint, boot, src, element?)Ce que embed.js exécute : afficher la première frame dans la console, attendre une intention d’utiliser la console, puis appeler boot
bootEmbedClient(options)Démarrer une application rendue côté client avec un routage en mémoire et une saisie clavier soumise aux règles de la page
bootEmbedPlayer(options)Démarrer le lecteur de frames pour une route avec rendu côté serveur
createHostInput(options)Saisie clavier qui suit les règles ci-dessus
createTextConsoleMount(config)Renvoie une fonction mount(element, shell?) pour <text-console>
reserveCss(cols, rows)Le CSS qui réserve l’espace de <text-console>

ENGAGE_SELECTOR et BYPASS_SELECTOR sont les sélecteurs des éléments qui démarrent l’application et des éléments qui conservent leurs propres touches.

Modifier cette page sur GitHub