diff --git a/.changeset/playground-build-chat.md b/.changeset/playground-build-chat.md
new file mode 100644
index 00000000..2e1aa670
--- /dev/null
+++ b/.changeset/playground-build-chat.md
@@ -0,0 +1,5 @@
+---
+"@cloudhal/cli": patch
+---
+
+Studio on a playground (RFC-0036): the Build page no longer says "This deployment can't reach Workers AI". It says Build with AI is available in your own install, since the playground leaves AI out, and links to deploying Cloudhal. Translations of the new sentence are AI drafts awaiting review.
diff --git a/apps/studio/src/components/BuildChat.tsx b/apps/studio/src/components/BuildChat.tsx
index 8e60e3bf..7374bde1 100644
--- a/apps/studio/src/components/BuildChat.tsx
+++ b/apps/studio/src/components/BuildChat.tsx
@@ -17,11 +17,20 @@ import {
SkeletonLine,
Text,
} from "@cloudhal/ui";
-import { GearSix, Sparkle, StopCircle, Target, WarningCircle, X } from "@phosphor-icons/react";
+import {
+ CloudArrowUp,
+ GearSix,
+ Sparkle,
+ StopCircle,
+ Target,
+ WarningCircle,
+ X,
+} from "@phosphor-icons/react";
import { useQuery } from "@tanstack/react-query";
import { useRef, useState } from "react";
import { listOf, useT } from "../i18n/index.tsx";
import { api, errorCode } from "../lib/api.ts";
+import { usePlayground } from "./usePlayground.ts";
/**
* The model Build recommends (RFC-0020 open question 7): it finished every brief of the Build
@@ -173,6 +182,8 @@ export function BuildChat({
}
};
+ // RFC-0036 §5: a playground has no AI by design; say so, not that the deployment is broken.
+ const playground = usePlayground();
const ai = settings.data;
const reachable = ai !== undefined && ai.transport !== "none";
const on = reachable && ai.settings.enabled;
@@ -182,23 +193,12 @@ export function BuildChat({
icon={}
description={on ? t("build.chat.description") : undefined}
>
- {settings.isPending ? (
-
@@ -269,3 +269,55 @@ export function BuildChat({
);
}
+
+/**
+ * Build chat while it cannot run: loading, a playground (RFC-0036 §5 leaves AI out), a deployment
+ * that cannot reach Workers AI, or AI turned off for the site.
+ */
+function BuildChatOff({
+ siteId,
+ deployUrl,
+ reachable,
+ pending,
+}: {
+ siteId: string;
+ deployUrl: string | undefined;
+ reachable: boolean;
+ pending: boolean;
+}) {
+ const t = useT("build");
+ if (deployUrl !== undefined)
+ return (
+
}
+ title={t("playground.unavailableTitle")}
+ description={t("build.chat.playgroundHint")}
+ contents={
+
+ {t("playground.deploy")}
+
+ }
+ />
+ );
+ if (pending)
+ return (
+
+
+
+
+ );
+ return (
+
}
+ title={reachable ? t("build.chat.off") : t("build.chat.unavailable")}
+ description={reachable ? t("build.chat.offHint") : t("build.chat.unavailableHint")}
+ contents={
+ reachable ? (
+
+ {t("build.chat.settings")}
+
+ ) : undefined
+ }
+ />
+ );
+}
diff --git a/apps/studio/src/i18n/lazy/build.ar.ts b/apps/studio/src/i18n/lazy/build.ar.ts
index 2770c7b3..6dfad2cc 100644
--- a/apps/studio/src/i18n/lazy/build.ar.ts
+++ b/apps/studio/src/i18n/lazy/build.ar.ts
@@ -1,6 +1,6 @@
// Generated by `pnpm i18n:import` (ADR-0034): ar `build.*` messages.
// Do not edit: change the translation file and import it; only English is edited here.
-// hash: b0ecd5c89931da8f
+// hash: 76cf17aa187ebf47
import type { NamespaceMessages } from "../namespaces.ts";
export const messages: NamespaceMessages<"build"> = {
@@ -64,6 +64,8 @@ export const messages: NamespaceMessages<"build"> = {
unavailableHint:
"على Cloudflare، يستخدم الذكاء الاصطناعي ربط الـWorker؛ ومحليًا، اضبط CLOUDFLARE_ACCOUNT_ID وCLOUDHAL_AI_TOKEN.",
settings: "افتح الإعدادات",
+ playgroundHint:
+ "تعمل ميزة «ابنِ بالذكاء الاصطناعي» على Workers AI، وهو غير متاح في بيئة التجربة. انشر Cloudhal على حسابك في Cloudflare لاستخدامها.",
pick: "اختر",
picking: "انقر جزءًا من الصفحة",
pickLabel: "اختر جزءًا من الصفحة لتغييره",
diff --git a/apps/studio/src/i18n/lazy/build.de.ts b/apps/studio/src/i18n/lazy/build.de.ts
index 02ef6a73..092cee22 100644
--- a/apps/studio/src/i18n/lazy/build.de.ts
+++ b/apps/studio/src/i18n/lazy/build.de.ts
@@ -1,6 +1,6 @@
// Generated by `pnpm i18n:import` (ADR-0034): de `build.*` messages.
// Do not edit: change the translation file and import it; only English is edited here.
-// hash: 9570e1429079d5a5
+// hash: b7c6c44572c830c3
import type { NamespaceMessages } from "../namespaces.ts";
export const messages: NamespaceMessages<"build"> = {
@@ -67,6 +67,8 @@ export const messages: NamespaceMessages<"build"> = {
unavailableHint:
"Auf Cloudflare nutzt die KI das Binding des Workers; lokal setze CLOUDFLARE_ACCOUNT_ID und CLOUDHAL_AI_TOKEN.",
settings: "Einstellungen öffnen",
+ playgroundHint:
+ "„Mit KI bauen“ läuft mit Workers AI, das der Playground nicht enthält. Stell Cloudhal in deinem Cloudflare-Konto bereit, um es zu nutzen.",
pick: "Auswählen",
picking: "Klicke auf einen Teil der Seite",
pickLabel: "Wähle einen Teil der Seite, der geändert werden soll",
diff --git a/apps/studio/src/i18n/lazy/build.en.ts b/apps/studio/src/i18n/lazy/build.en.ts
index 341221f2..7f9a03ec 100644
--- a/apps/studio/src/i18n/lazy/build.en.ts
+++ b/apps/studio/src/i18n/lazy/build.en.ts
@@ -67,6 +67,9 @@ export const messages = {
unavailableHint:
"On Cloudflare, AI uses the Worker's binding; locally, set CLOUDFLARE_ACCOUNT_ID and CLOUDHAL_AI_TOKEN.",
settings: "Open settings",
+ /** RFC-0036 §5: the playground leaves AI out; under "Available in your own install". */
+ playgroundHint:
+ "“Build with AI” runs on Workers AI, which the playground leaves out. Deploy Cloudhal to your Cloudflare account to use it.",
/** RFC-0020 §6: picking a part of the page in the preview to aim the next brief at it. */
pick: "Pick",
picking: "Click a part of the page",
diff --git a/apps/studio/src/i18n/lazy/build.es.ts b/apps/studio/src/i18n/lazy/build.es.ts
index 771919b9..1c948171 100644
--- a/apps/studio/src/i18n/lazy/build.es.ts
+++ b/apps/studio/src/i18n/lazy/build.es.ts
@@ -1,6 +1,6 @@
// Generated by `pnpm i18n:import` (ADR-0034): es `build.*` messages.
// Do not edit: change the translation file and import it; only English is edited here.
-// hash: 539e2300f8e9449e
+// hash: c29d508c3fb28db9
import type { NamespaceMessages } from "../namespaces.ts";
export const messages: NamespaceMessages<"build"> = {
@@ -65,6 +65,8 @@ export const messages: NamespaceMessages<"build"> = {
unavailableHint:
"En Cloudflare, la IA usa el binding del Worker; en local, define CLOUDFLARE_ACCOUNT_ID y CLOUDHAL_AI_TOKEN.",
settings: "Abrir configuración",
+ playgroundHint:
+ "«Construir con IA» funciona con Workers AI, que el entorno de prueba no incluye. Despliega Cloudhal en tu cuenta de Cloudflare para usarlo.",
pick: "Elegir",
picking: "Haz clic en una parte de la página",
pickLabel: "Elige una parte de la página para cambiarla",
diff --git a/apps/studio/src/i18n/lazy/build.fa.ts b/apps/studio/src/i18n/lazy/build.fa.ts
index 2b5b539b..6b7581b8 100644
--- a/apps/studio/src/i18n/lazy/build.fa.ts
+++ b/apps/studio/src/i18n/lazy/build.fa.ts
@@ -1,6 +1,6 @@
// Generated by `pnpm i18n:import` (ADR-0034): fa `build.*` messages.
// Do not edit: change the translation file and import it; only English is edited here.
-// hash: 89a3f17b24c0c47e
+// hash: 20f5bf17b4ff1a54
import type { NamespaceMessages } from "../namespaces.ts";
export const messages: NamespaceMessages<"build"> = {
@@ -65,6 +65,8 @@ export const messages: NamespaceMessages<"build"> = {
unavailableHint:
"در Cloudflare، هوش مصنوعی از binding مربوط به Worker استفاده میکند؛ بهصورت محلی، CLOUDFLARE_ACCOUNT_ID و CLOUDHAL_AI_TOKEN را تنظیم کنید.",
settings: "باز کردن تنظیمات",
+ playgroundHint:
+ "«ساخت با هوش مصنوعی» با Workers AI کار میکند که در محیط آزمایشی در دسترس نیست. برای استفاده، Cloudhal را روی حساب Cloudflare خود مستقر کنید.",
pick: "انتخاب",
picking: "روی بخشی از صفحه کلیک کنید",
pickLabel: "بخشی از صفحه را برای تغییر انتخاب کنید",
diff --git a/apps/studio/src/i18n/lazy/build.fr.ts b/apps/studio/src/i18n/lazy/build.fr.ts
index 60f5f2f1..4f5aab98 100644
--- a/apps/studio/src/i18n/lazy/build.fr.ts
+++ b/apps/studio/src/i18n/lazy/build.fr.ts
@@ -1,6 +1,6 @@
// Generated by `pnpm i18n:import` (ADR-0034): fr `build.*` messages.
// Do not edit: change the translation file and import it; only English is edited here.
-// hash: 7bf6286efefe63e4
+// hash: 586c485924667471
import type { NamespaceMessages } from "../namespaces.ts";
export const messages: NamespaceMessages<"build"> = {
@@ -65,6 +65,8 @@ export const messages: NamespaceMessages<"build"> = {
unavailableHint:
"Sur Cloudflare, l’IA utilise la liaison du Worker ; en local, définissez CLOUDFLARE_ACCOUNT_ID et CLOUDHAL_AI_TOKEN.",
settings: "Ouvrir les paramètres",
+ playgroundHint:
+ "« Construire avec l’IA » fonctionne avec Workers AI, absent du bac à sable. Déployez Cloudhal sur votre compte Cloudflare pour l’utiliser.",
pick: "Choisir",
picking: "Cliquez sur une partie de la page",
pickLabel: "Choisir une partie de la page à modifier",
diff --git a/apps/studio/src/i18n/lazy/build.he.ts b/apps/studio/src/i18n/lazy/build.he.ts
index ffdfe0d6..3b833656 100644
--- a/apps/studio/src/i18n/lazy/build.he.ts
+++ b/apps/studio/src/i18n/lazy/build.he.ts
@@ -1,6 +1,6 @@
// Generated by `pnpm i18n:import` (ADR-0034): he `build.*` messages.
// Do not edit: change the translation file and import it; only English is edited here.
-// hash: baceb92d6ce0cbae
+// hash: 0d362b9b30930225
import type { NamespaceMessages } from "../namespaces.ts";
export const messages: NamespaceMessages<"build"> = {
@@ -65,6 +65,8 @@ export const messages: NamespaceMessages<"build"> = {
unavailableHint:
"ב-Cloudflare, ה-AI משתמש ב-binding של ה-Worker; בהרצה מקומית יש להגדיר CLOUDFLARE_ACCOUNT_ID ו-CLOUDHAL_AI_TOKEN.",
settings: "פתיחת ההגדרות",
+ playgroundHint:
+ "„בנייה עם AI” פועלת עם Workers AI, שאינו זמין בסביבת הניסיון. פרסו את Cloudhal בחשבון Cloudflare שלכם כדי להשתמש בה.",
pick: "בחירה",
picking: "יש ללחוץ על חלק בעמוד",
pickLabel: "בחירת חלק בעמוד לשינוי",
diff --git a/apps/studio/src/i18n/lazy/build.hi.ts b/apps/studio/src/i18n/lazy/build.hi.ts
index 3971a8a4..4ac9ab67 100644
--- a/apps/studio/src/i18n/lazy/build.hi.ts
+++ b/apps/studio/src/i18n/lazy/build.hi.ts
@@ -1,6 +1,6 @@
// Generated by `pnpm i18n:import` (ADR-0034): hi `build.*` messages.
// Do not edit: change the translation file and import it; only English is edited here.
-// hash: b5ef9c4227e59fa6
+// hash: 1e8a7b7a3eac2abb
import type { NamespaceMessages } from "../namespaces.ts";
export const messages: NamespaceMessages<"build"> = {
@@ -64,6 +64,8 @@ export const messages: NamespaceMessages<"build"> = {
unavailableHint:
"Cloudflare पर AI, Worker की बाइंडिंग का उपयोग करता है; लोकल में CLOUDFLARE_ACCOUNT_ID और CLOUDHAL_AI_TOKEN सेट करें।",
settings: "सेटिंग्स खोलें",
+ playgroundHint:
+ "“AI के साथ बनाएँ” Workers AI पर चलता है, जो प्लेग्राउंड में शामिल नहीं है। इसे इस्तेमाल करने के लिए Cloudhal को अपने Cloudflare खाते पर डिप्लॉय करें।",
pick: "चुनें",
picking: "पेज के किसी हिस्से पर क्लिक करें",
pickLabel: "बदलने के लिए पेज का कोई हिस्सा चुनें",
diff --git a/apps/studio/src/i18n/lazy/build.id.ts b/apps/studio/src/i18n/lazy/build.id.ts
index 0361c050..efcf23dc 100644
--- a/apps/studio/src/i18n/lazy/build.id.ts
+++ b/apps/studio/src/i18n/lazy/build.id.ts
@@ -1,6 +1,6 @@
// Generated by `pnpm i18n:import` (ADR-0034): id `build.*` messages.
// Do not edit: change the translation file and import it; only English is edited here.
-// hash: ceff689a611375d7
+// hash: 49c5c6fbb0801fd8
import type { NamespaceMessages } from "../namespaces.ts";
export const messages: NamespaceMessages<"build"> = {
@@ -65,6 +65,8 @@ export const messages: NamespaceMessages<"build"> = {
unavailableHint:
"Di Cloudflare, AI menggunakan binding Worker; secara lokal, setel CLOUDFLARE_ACCOUNT_ID dan CLOUDHAL_AI_TOKEN.",
settings: "Buka pengaturan",
+ playgroundHint:
+ "“Bangun dengan AI” berjalan dengan Workers AI, yang tidak tersedia di playground. Deploy Cloudhal ke akun Cloudflare Anda untuk menggunakannya.",
pick: "Pilih",
picking: "Klik bagian halaman",
pickLabel: "Pilih bagian halaman yang akan diubah",
diff --git a/apps/studio/src/i18n/lazy/build.it.ts b/apps/studio/src/i18n/lazy/build.it.ts
index e26c2024..400ff16c 100644
--- a/apps/studio/src/i18n/lazy/build.it.ts
+++ b/apps/studio/src/i18n/lazy/build.it.ts
@@ -1,6 +1,6 @@
// Generated by `pnpm i18n:import` (ADR-0034): it `build.*` messages.
// Do not edit: change the translation file and import it; only English is edited here.
-// hash: 0a2ed752e84007b1
+// hash: 8161d3b900dd7ae1
import type { NamespaceMessages } from "../namespaces.ts";
export const messages: NamespaceMessages<"build"> = {
@@ -65,6 +65,8 @@ export const messages: NamespaceMessages<"build"> = {
unavailableHint:
"Su Cloudflare, l’IA usa il binding del Worker; in locale, imposta CLOUDFLARE_ACCOUNT_ID e CLOUDHAL_AI_TOKEN.",
settings: "Apri impostazioni",
+ playgroundHint:
+ "«Costruisci con l’IA» usa Workers AI, che il playground non include. Distribuisci Cloudhal sul tuo account Cloudflare per usarlo.",
pick: "Scegli",
picking: "Fai clic su una parte della pagina",
pickLabel: "Scegli una parte della pagina da modificare",
diff --git a/apps/studio/src/i18n/lazy/build.ja.ts b/apps/studio/src/i18n/lazy/build.ja.ts
index fdb61fc2..719ea5f7 100644
--- a/apps/studio/src/i18n/lazy/build.ja.ts
+++ b/apps/studio/src/i18n/lazy/build.ja.ts
@@ -1,6 +1,6 @@
// Generated by `pnpm i18n:import` (ADR-0034): ja `build.*` messages.
// Do not edit: change the translation file and import it; only English is edited here.
-// hash: 7a19f28deaf25224
+// hash: 2480de060975ba8e
import type { NamespaceMessages } from "../namespaces.ts";
export const messages: NamespaceMessages<"build"> = {
@@ -65,6 +65,8 @@ export const messages: NamespaceMessages<"build"> = {
unavailableHint:
"Cloudflare上ではAIはWorkerのバインディングを使用します。ローカルではCLOUDFLARE_ACCOUNT_IDとCLOUDHAL_AI_TOKENを設定してください。",
settings: "設定を開く",
+ playgroundHint:
+ "「AIでビルド」は Workers AI で動作しますが、プレイグラウンドでは使えません。使うには、Cloudhal をご自身の Cloudflare アカウントにデプロイしてください。",
pick: "選択",
picking: "ページの一部をクリックしてください",
pickLabel: "変更するページの部分を選択",
diff --git a/apps/studio/src/i18n/lazy/build.ko.ts b/apps/studio/src/i18n/lazy/build.ko.ts
index cdbd9789..75ea6162 100644
--- a/apps/studio/src/i18n/lazy/build.ko.ts
+++ b/apps/studio/src/i18n/lazy/build.ko.ts
@@ -1,6 +1,6 @@
// Generated by `pnpm i18n:import` (ADR-0034): ko `build.*` messages.
// Do not edit: change the translation file and import it; only English is edited here.
-// hash: cecb495c1905e961
+// hash: c5fd0228bea665db
import type { NamespaceMessages } from "../namespaces.ts";
export const messages: NamespaceMessages<"build"> = {
@@ -65,6 +65,8 @@ export const messages: NamespaceMessages<"build"> = {
unavailableHint:
"Cloudflare에서는 AI가 Worker의 바인딩을 사용해요. 로컬에서는 CLOUDFLARE_ACCOUNT_ID와 CLOUDHAL_AI_TOKEN을 설정하세요.",
settings: "설정 열기",
+ playgroundHint:
+ "‘AI로 빌드’는 Workers AI로 동작하며, 플레이그라운드에서는 제공되지 않아요. 사용하려면 Cloudhal을 내 Cloudflare 계정에 배포하세요.",
pick: "선택",
picking: "페이지의 한 부분을 클릭하세요",
pickLabel: "변경할 페이지 부분 선택",
diff --git a/apps/studio/src/i18n/lazy/build.nl.ts b/apps/studio/src/i18n/lazy/build.nl.ts
index 6ca2e48e..6fa17188 100644
--- a/apps/studio/src/i18n/lazy/build.nl.ts
+++ b/apps/studio/src/i18n/lazy/build.nl.ts
@@ -1,6 +1,6 @@
// Generated by `pnpm i18n:import` (ADR-0034): nl `build.*` messages.
// Do not edit: change the translation file and import it; only English is edited here.
-// hash: 3c48f03e4d53a37b
+// hash: 16ffac83d1598bed
import type { NamespaceMessages } from "../namespaces.ts";
export const messages: NamespaceMessages<"build"> = {
@@ -65,6 +65,8 @@ export const messages: NamespaceMessages<"build"> = {
unavailableHint:
"Op Cloudflare gebruikt AI de binding van de Worker; lokaal stel je CLOUDFLARE_ACCOUNT_ID en CLOUDHAL_AI_TOKEN in.",
settings: "Instellingen openen",
+ playgroundHint:
+ "‘Bouwen met AI’ draait op Workers AI, dat de playground niet heeft. Implementeer Cloudhal in je Cloudflare-account om het te gebruiken.",
pick: "Kiezen",
picking: "Klik op een deel van de pagina",
pickLabel: "Kies een deel van de pagina om te wijzigen",
diff --git a/apps/studio/src/i18n/lazy/build.pl.ts b/apps/studio/src/i18n/lazy/build.pl.ts
index e4da69c3..e98113ff 100644
--- a/apps/studio/src/i18n/lazy/build.pl.ts
+++ b/apps/studio/src/i18n/lazy/build.pl.ts
@@ -1,6 +1,6 @@
// Generated by `pnpm i18n:import` (ADR-0034): pl `build.*` messages.
// Do not edit: change the translation file and import it; only English is edited here.
-// hash: 76bae465989fd6de
+// hash: 5ef87d1149210466
import type { NamespaceMessages } from "../namespaces.ts";
export const messages: NamespaceMessages<"build"> = {
@@ -65,6 +65,8 @@ export const messages: NamespaceMessages<"build"> = {
unavailableHint:
"Na Cloudflare AI korzysta z powiązania Workera; lokalnie ustaw CLOUDFLARE_ACCOUNT_ID i CLOUDHAL_AI_TOKEN.",
settings: "Otwórz ustawienia",
+ playgroundHint:
+ "„Buduj z AI” działa na Workers AI, którego nie ma w piaskownicy. Wdróż Cloudhal na swoim koncie Cloudflare, aby z tego korzystać.",
pick: "Wskaż",
picking: "Kliknij część strony",
pickLabel: "Wskaż część strony do zmiany",
diff --git a/apps/studio/src/i18n/lazy/build.pt-BR.ts b/apps/studio/src/i18n/lazy/build.pt-BR.ts
index 9d1f086c..71d2fa13 100644
--- a/apps/studio/src/i18n/lazy/build.pt-BR.ts
+++ b/apps/studio/src/i18n/lazy/build.pt-BR.ts
@@ -1,6 +1,6 @@
// Generated by `pnpm i18n:import` (ADR-0034): pt-BR `build.*` messages.
// Do not edit: change the translation file and import it; only English is edited here.
-// hash: 3fb4aff31607b7e0
+// hash: f61046a12773dbbe
import type { NamespaceMessages } from "../namespaces.ts";
export const messages: NamespaceMessages<"build"> = {
@@ -65,6 +65,8 @@ export const messages: NamespaceMessages<"build"> = {
unavailableHint:
"Na Cloudflare, a IA usa o binding do Worker; localmente, defina CLOUDFLARE_ACCOUNT_ID e CLOUDHAL_AI_TOKEN.",
settings: "Abrir configurações",
+ playgroundHint:
+ "“Criar com IA” usa o Workers AI, que o playground não inclui. Implante o Cloudhal na sua conta da Cloudflare para usar.",
pick: "Escolher",
picking: "Clique em uma parte da página",
pickLabel: "Escolha uma parte da página para alterar",
diff --git a/apps/studio/src/i18n/lazy/build.ru.ts b/apps/studio/src/i18n/lazy/build.ru.ts
index 31e77946..c80454c4 100644
--- a/apps/studio/src/i18n/lazy/build.ru.ts
+++ b/apps/studio/src/i18n/lazy/build.ru.ts
@@ -1,6 +1,6 @@
// Generated by `pnpm i18n:import` (ADR-0034): ru `build.*` messages.
// Do not edit: change the translation file and import it; only English is edited here.
-// hash: b052b8b8d9d66fa3
+// hash: e28759009d1d9bc3
import type { NamespaceMessages } from "../namespaces.ts";
export const messages: NamespaceMessages<"build"> = {
@@ -65,6 +65,8 @@ export const messages: NamespaceMessages<"build"> = {
unavailableHint:
"В Cloudflare ИИ использует привязку Worker; локально задайте CLOUDFLARE_ACCOUNT_ID и CLOUDHAL_AI_TOKEN.",
settings: "Открыть настройки",
+ playgroundHint:
+ "«Собрать с ИИ» работает на Workers AI, которого нет в песочнице. Разверните Cloudhal в своём аккаунте Cloudflare, чтобы пользоваться этим.",
pick: "Выбрать",
picking: "Нажмите на часть страницы",
pickLabel: "Выберите часть страницы для изменения",
diff --git a/apps/studio/src/i18n/lazy/build.tr.ts b/apps/studio/src/i18n/lazy/build.tr.ts
index ead88940..01c19347 100644
--- a/apps/studio/src/i18n/lazy/build.tr.ts
+++ b/apps/studio/src/i18n/lazy/build.tr.ts
@@ -1,6 +1,6 @@
// Generated by `pnpm i18n:import` (ADR-0034): tr `build.*` messages.
// Do not edit: change the translation file and import it; only English is edited here.
-// hash: 5a73ae5dc97a2d58
+// hash: e40593bff61b53c5
import type { NamespaceMessages } from "../namespaces.ts";
export const messages: NamespaceMessages<"build"> = {
@@ -65,6 +65,8 @@ export const messages: NamespaceMessages<"build"> = {
unavailableHint:
"Cloudflare'de AI, Worker'ın bağlantısını kullanır; yerelde CLOUDFLARE_ACCOUNT_ID ve CLOUDHAL_AI_TOKEN ayarlayın.",
settings: "Ayarları aç",
+ playgroundHint:
+ "“AI ile kur” Workers AI ile çalışır; deneme alanında bu yok. Kullanmak için Cloudhal'ı kendi Cloudflare hesabına kur.",
pick: "Seç",
picking: "Sayfadan bir parçaya tıklayın",
pickLabel: "Değiştirilecek parçayı sayfadan seçin",
diff --git a/apps/studio/src/i18n/lazy/build.uk.ts b/apps/studio/src/i18n/lazy/build.uk.ts
index bbc3bef6..0023ae2b 100644
--- a/apps/studio/src/i18n/lazy/build.uk.ts
+++ b/apps/studio/src/i18n/lazy/build.uk.ts
@@ -1,6 +1,6 @@
// Generated by `pnpm i18n:import` (ADR-0034): uk `build.*` messages.
// Do not edit: change the translation file and import it; only English is edited here.
-// hash: edc36e6bae69a974
+// hash: a26e6b67c3f73605
import type { NamespaceMessages } from "../namespaces.ts";
export const messages: NamespaceMessages<"build"> = {
@@ -65,6 +65,8 @@ export const messages: NamespaceMessages<"build"> = {
unavailableHint:
"У Cloudflare ШІ використовує прив’язку Worker; локально задайте CLOUDFLARE_ACCOUNT_ID і CLOUDHAL_AI_TOKEN.",
settings: "Відкрити налаштування",
+ playgroundHint:
+ "«Створити з ШІ» працює на Workers AI, якого немає в пісочниці. Розгорніть Cloudhal у своєму обліковому записі Cloudflare, щоб ним користуватися.",
pick: "Вибрати",
picking: "Клацніть частину сторінки",
pickLabel: "Виберіть частину сторінки для зміни",
diff --git a/apps/studio/src/i18n/lazy/build.zh-Hans.ts b/apps/studio/src/i18n/lazy/build.zh-Hans.ts
index 43bd7bcc..7aa25251 100644
--- a/apps/studio/src/i18n/lazy/build.zh-Hans.ts
+++ b/apps/studio/src/i18n/lazy/build.zh-Hans.ts
@@ -1,6 +1,6 @@
// Generated by `pnpm i18n:import` (ADR-0034): zh-Hans `build.*` messages.
// Do not edit: change the translation file and import it; only English is edited here.
-// hash: e6ce561d33a31f3b
+// hash: 7e6f643ab395193b
import type { NamespaceMessages } from "../namespaces.ts";
export const messages: NamespaceMessages<"build"> = {
@@ -63,6 +63,8 @@ export const messages: NamespaceMessages<"build"> = {
unavailableHint:
"在 Cloudflare 上,AI 使用 Worker 的绑定;在本地,请设置 CLOUDFLARE_ACCOUNT_ID 和 CLOUDHAL_AI_TOKEN。",
settings: "打开设置",
+ playgroundHint:
+ "“用 AI 构建”依赖 Workers AI,而演练场不提供它。将 Cloudhal 部署到你的 Cloudflare 账户即可使用。",
pick: "选取",
picking: "点击页面的某个部分",
pickLabel: "选取要修改的页面部分",
diff --git a/apps/studio/test/build-chat.test.tsx b/apps/studio/test/build-chat.test.tsx
index 6b816133..62e12af7 100644
--- a/apps/studio/test/build-chat.test.tsx
+++ b/apps/studio/test/build-chat.test.tsx
@@ -5,7 +5,7 @@
import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import { BuildChat } from "../src/components/BuildChat.tsx";
-import { wrap } from "./roles-fixtures.tsx";
+import { INSTANCE, wrap } from "./roles-fixtures.tsx";
afterEach(() => {
cleanup();
@@ -32,9 +32,25 @@ function ndjson(events: unknown[]): Response {
);
}
-function serve(build: () => Response, enabled = true, transport = "binding") {
+const PLAYGROUND = {
+ ...INSTANCE,
+ features: {
+ ...INSTANCE.features,
+ playground: {
+ expiresAt: "2099-01-01T00:00:00Z",
+ resetUrl: "/_playground/reset",
+ deployUrl: "https://example.test/deploy",
+ },
+ },
+};
+
+function serve(build: () => Response, enabled = true, transport = "binding", instance = INSTANCE) {
const fn = vi.fn(async (url: string, _init?: RequestInit) =>
- url.endsWith("/ai/settings") ? settings(enabled, transport) : build(),
+ url.endsWith("/ai/settings")
+ ? settings(enabled, transport)
+ : url.endsWith("/api/instance")
+ ? Response.json(instance)
+ : build(),
);
vi.stubGlobal("fetch", fn);
return fn;
@@ -57,6 +73,20 @@ describe("Build chat", () => {
expect(await screen.findByText("This deployment can't reach Workers AI")).toBeInTheDocument();
});
+ it("on a playground, says AI is left out and links to deploying (RFC-0036 §5)", async () => {
+ serve(() => new Response(null, { status: 404 }), false, "none", PLAYGROUND);
+ render(wrap(
));
+ expect(await screen.findByText("Available in your own install")).toBeInTheDocument();
+ expect(
+ screen.getByText(/runs on Workers AI, which the playground leaves out/),
+ ).toBeInTheDocument();
+ expect(screen.queryByText("This deployment can't reach Workers AI")).toBeNull();
+ expect(screen.getByRole("link", { name: /Deploy to Cloudflare/ })).toHaveAttribute(
+ "href",
+ "https://example.test/deploy",
+ );
+ });
+
it("sends the brief and shows the model's notes and the outcome", async () => {
const fetch = serve(() =>
ndjson([
@@ -174,12 +204,19 @@ describe("Build chat", () => {
unmount();
vi.stubGlobal(
"fetch",
- vi.fn(async () =>
- Response.json({
- settings: { enabled: true, model: "@cf/openai/gpt-oss-120b", dailyLimit: 100, rev: 1 },
- usage: { requests: 0 },
- transport: "binding",
- }),
+ vi.fn(async (url: string) =>
+ url.endsWith("/api/instance")
+ ? Response.json(INSTANCE)
+ : Response.json({
+ settings: {
+ enabled: true,
+ model: "@cf/openai/gpt-oss-120b",
+ dailyLimit: 100,
+ rev: 1,
+ },
+ usage: { requests: 0 },
+ transport: "binding",
+ }),
),
);
render(wrap(
));
diff --git a/scripts/size-baseline.json b/scripts/size-baseline.json
index 52f2f80d..b977bbf3 100644
--- a/scripts/size-baseline.json
+++ b/scripts/size-baseline.json
@@ -67,7 +67,7 @@
"lines": 214
},
"apps/studio/src/components/BuildChat.tsx::BuildChat": {
- "lines": 209
+ "lines": 200
},
"apps/studio/src/components/ChangeAgentModeDialog.tsx::ChangeAgentModeDialog": {
"lines": 178