diff --git a/app/src-tauri/tauri.conf.json b/app/src-tauri/tauri.conf.json
index 190d0370..c36e5e8e 100644
--- a/app/src-tauri/tauri.conf.json
+++ b/app/src-tauri/tauri.conf.json
@@ -21,7 +21,7 @@
}
],
"security": {
- "csp": "default-src 'self'; connect-src 'self' ipc: http://ipc.localhost ws://127.0.0.1:*; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; frame-src blob:; font-src 'self'"
+ "csp": "default-src 'self'; connect-src 'self' ipc: http://ipc.localhost ws://127.0.0.1:*; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; frame-src blob: http://127.0.0.1:*; font-src 'self'"
}
},
"plugins": {
diff --git a/app/src/dev/preview.ts b/app/src/dev/preview.ts
index e397c48a..c59f0a5c 100644
--- a/app/src/dev/preview.ts
+++ b/app/src/dev/preview.ts
@@ -14,6 +14,7 @@ import { seedBrowser } from "./seedBrowser";
import { seedChats } from "./seedChats";
import { seedChief } from "./seedChief";
import { seedRoutines } from "./seedRoutines";
+import { seedScreens } from "./seedScreens";
export function previewProps(search: string): { host: Host; connect: Connect } {
const params = new URLSearchParams(search);
@@ -113,6 +114,7 @@ function seed(fake: FakeBotloft): void {
});
seedRoutines(fake, { scout: scout.id, analyst: analyst.id });
seedBrowser(fake, scout.id);
+ seedScreens(fake, research.id);
const ops = fake.addCrew("Ops");
const deploy = fake.addBot(ops.id, "Deploy", "Ships the site on Fridays");
const watcher = fake.addBot(ops.id, "Watcher", "Keeps an eye on the error log");
diff --git a/app/src/dev/seedScreens.ts b/app/src/dev/seedScreens.ts
new file mode 100644
index 00000000..f9b297d6
--- /dev/null
+++ b/app/src/dev/seedScreens.ts
@@ -0,0 +1,72 @@
+// Dev only: a Designer drawing screens in the fake preview, so the design
+// area has something live to show. The page arrives in pieces, as the
+// daemon's drafts do while the model writes it.
+
+import type { FakeBotloft } from "../lib/fake";
+import type { CrewId } from "../lib/protocol.gen";
+
+const LANDING = `
+
Padaria Aurora
+
+
+
+Pão quente, todo dia às 6h.
+
Fermentação natural de 36 horas, farinha orgânica e um forno a lenha que não apaga desde 1987.
+
Ver o cardápio
+SourdoughCasca crocante, miolo aberto.
+Croissant81 camadas de manteiga francesa.
+Bolo de fubáA receita da vó, sem mudar nada.
+`;
+
+const APP = `
+
+Encomendas
+2 SourdoughR$ 38
+6 CroissantsR$ 54
+Bolo de fubáR$ 29
`;
+
+/** A Designer that draws the landing page again and again. */
+export function seedScreens(fake: FakeBotloft, crewId: CrewId): void {
+ const designer = fake.addBot(crewId, "Designer", "Draws the bakery's screens");
+ fake.setBotState(designer.id, "busy", 1);
+ fake.screens.add(designer.id, "encomendas.html", APP, {
+ folder: "site",
+ device: "mobile",
+ at: fake.now - 60_000,
+ });
+ const path = "C:\\Work\\site\\index.html";
+ let written = 0;
+ let resting = 0;
+ setInterval(() => {
+ if (resting > 0) {
+ resting -= 1;
+ if (resting === 0) {
+ written = 0;
+ }
+ return;
+ }
+ written = Math.min(LANDING.length, written + 70);
+ const done = written === LANDING.length;
+ if (done) {
+ fake.screens.add(designer.id, "index.html", LANDING, { folder: "site" });
+ resting = 30;
+ }
+ fake.screens.draft(designer.id, path, LANDING.slice(0, written), done);
+ }, 150);
+}
diff --git a/app/src/features/bots/BotHeader.tsx b/app/src/features/bots/BotHeader.tsx
index b93bbdb4..4da45aff 100644
--- a/app/src/features/bots/BotHeader.tsx
+++ b/app/src/features/bots/BotHeader.tsx
@@ -5,6 +5,7 @@ import {
Files,
FolderOpen,
Globe,
+ LayoutTemplate,
PanelRight,
Pause,
Pencil,
@@ -40,6 +41,9 @@ export function BotHeader({
browserOpen,
browsing,
onToggleBrowser,
+ screensOpen,
+ drawing,
+ onToggleScreens,
}: {
bot: Bot;
crew: Crew;
@@ -53,6 +57,10 @@ export function BotHeader({
/** The bot's browser is open (spec 21.8). */
browsing: boolean;
onToggleBrowser(): void;
+ screensOpen: boolean;
+ /** The bot is writing a screen (spec 22.5). */
+ drawing: boolean;
+ onToggleScreens(): void;
}) {
const t = useT();
const words = t.bots.header;
@@ -115,6 +123,28 @@ export function BotHeader({
+
+
+ {drawing && !screensOpen && (
+
+ )}
+
)}
+ {showScreen && tool.file && isScreenFile(tool.file) && tool.status !== "failed" && (
+
+ )}
{showBrowser && isBrowserTool(tool.name) && (