diff --git a/README.ar.md b/README.ar.md index e27b5501e0..75ddaef84a 100644 --- a/README.ar.md +++ b/README.ar.md @@ -1,9 +1,9 @@ - + # Codewhale Codewhale وكيل مفتوح المصدر يقرأ مشروعك ويعدّل الملفات ويشغّل الأوامر ويتحقق من عمله باستخدام نموذج مستضاف أو محلي تختاره. ابدأ بمهمة واحدة في الطرفية. وللأعمال الأكبر، وزّع أجزاء العمل على وكلاء بنماذج وأدوار مختلفة. -![Codewhale يعمل في طرفية](web/public/codewhale-tui-d7a9a1c.png) +![Codewhale يعمل في طرفية](web/public/codewhale-tui-5765d80.png) *معاينة للطرفية من بنية تطوير للإصدار v0.10.0.* diff --git a/README.ca.md b/README.ca.md index 8b2b3768b5..36d14dd871 100644 --- a/README.ca.md +++ b/README.ca.md @@ -1,9 +1,9 @@ - + # Codewhale Codewhale és un agent de codi obert que llegeix el teu projecte, edita fitxers, executa ordres i comprova la seva feina amb un model allotjat o local que tu tries. Comença amb una tasca al terminal. Per a una feina més gran, assigna parts de la feina a agents amb models i rols diferents. -![Codewhale executant-se en un terminal](web/public/codewhale-tui-d7a9a1c.png) +![Codewhale executant-se en un terminal](web/public/codewhale-tui-5765d80.png) *Previsualització del terminal d’una compilació de desenvolupament de la v0.10.0.* diff --git a/README.de.md b/README.de.md index 941c033fd5..04a5d1471a 100644 --- a/README.de.md +++ b/README.de.md @@ -1,9 +1,9 @@ - + # Codewhale Codewhale ist ein Open-Source-Agent, der dein Projekt liest, Dateien bearbeitet, Befehle ausführt und seine Arbeit mit einem gehosteten oder lokalen Modell deiner Wahl prüft. Starte mit einer Aufgabe im Terminal. Teile eine größere Aufgabe auf Agenten mit verschiedenen Modellen und Rollen auf. -![Codewhale in einem Terminal](web/public/codewhale-tui-d7a9a1c.png) +![Codewhale in einem Terminal](web/public/codewhale-tui-5765d80.png) *Terminalvorschau aus einem Entwicklungsbuild von v0.10.0.* diff --git a/README.es-419.md b/README.es-419.md index c023c03317..23ade4338f 100644 --- a/README.es-419.md +++ b/README.es-419.md @@ -1,9 +1,9 @@ - + # Codewhale Codewhale es un agente de código abierto que lee tu proyecto, edita archivos, ejecuta comandos y comprueba su trabajo con un modelo alojado o local que tú eliges. Empieza con una tarea en la terminal. Para un trabajo más grande, asigna partes del trabajo a agentes con distintos modelos y roles. -![Codewhale ejecutándose en una terminal](web/public/codewhale-tui-d7a9a1c.png) +![Codewhale ejecutándose en una terminal](web/public/codewhale-tui-5765d80.png) *Vista previa de la terminal de una compilación de desarrollo de v0.10.0.* diff --git a/README.fr.md b/README.fr.md index 76c01f30ce..be2a889766 100644 --- a/README.fr.md +++ b/README.fr.md @@ -1,9 +1,9 @@ - + # Codewhale Codewhale est un agent open source qui lit votre projet, modifie des fichiers, exécute des commandes et vérifie son travail avec un modèle hébergé ou local de votre choix. Commencez par une tâche dans votre terminal. Pour un travail plus important, confiez-en des parties à des agents utilisant différents modèles et rôles. -![Codewhale en cours d’exécution dans un terminal](web/public/codewhale-tui-d7a9a1c.png) +![Codewhale en cours d’exécution dans un terminal](web/public/codewhale-tui-5765d80.png) *Aperçu du terminal dans une version de développement de la v0.10.0.* diff --git a/README.hi.md b/README.hi.md index 8b37a3ae7b..fe959a8020 100644 --- a/README.hi.md +++ b/README.hi.md @@ -1,9 +1,9 @@ - + # Codewhale Codewhale एक ओपन सोर्स एजेंट है जो आपकी पसंद के होस्ट किए गए या लोकल मॉडल से आपका प्रोजेक्ट पढ़ता है, फ़ाइलें संपादित करता है, कमांड चलाता है और अपने काम की जाँच करता है। टर्मिनल में एक काम से शुरुआत करें। बड़े काम के हिस्से अलग-अलग मॉडल और भूमिकाओं वाले एजेंटों को सौंपें। -![टर्मिनल में चलता Codewhale](web/public/codewhale-tui-d7a9a1c.png) +![टर्मिनल में चलता Codewhale](web/public/codewhale-tui-5765d80.png) *v0.10.0 के विकासाधीन बिल्ड से टर्मिनल का पूर्वावलोकन।* diff --git a/README.id.md b/README.id.md index 3a23ee4e58..7aa525fbcb 100644 --- a/README.id.md +++ b/README.id.md @@ -1,9 +1,9 @@ - + # Codewhale Codewhale adalah agen sumber terbuka yang membaca proyek, mengedit berkas, menjalankan perintah, dan memeriksa hasil kerjanya dengan model yang dihosting atau model lokal pilihan Anda. Mulailah dengan satu tugas di terminal. Untuk pekerjaan yang lebih besar, bagikan sebagian pekerjaan kepada agen dengan model dan peran yang berbeda. -![Codewhale berjalan di terminal](web/public/codewhale-tui-d7a9a1c.png) +![Codewhale berjalan di terminal](web/public/codewhale-tui-5765d80.png) *Pratinjau terminal dari build pengembangan v0.10.0.* diff --git a/README.it.md b/README.it.md index 0108f83f52..9171aa0b1d 100644 --- a/README.it.md +++ b/README.it.md @@ -1,9 +1,9 @@ - + # Codewhale Codewhale è un agente open source che legge il tuo progetto, modifica file, esegue comandi e verifica il proprio lavoro usando un modello ospitato o locale a tua scelta. Parti da un’attività nel terminale. Per un lavoro più grande, assegna parti del lavoro ad agenti con modelli e ruoli diversi. -![Codewhale in esecuzione in un terminale](web/public/codewhale-tui-d7a9a1c.png) +![Codewhale in esecuzione in un terminale](web/public/codewhale-tui-5765d80.png) *Anteprima del terminale da una build di sviluppo della v0.10.0.* diff --git a/README.ja-JP.md b/README.ja-JP.md index 9b3f17cce2..6da8f04881 100644 --- a/README.ja-JP.md +++ b/README.ja-JP.md @@ -1,9 +1,9 @@ - + # Codewhale Codewhale は、選んだホスト型またはローカルのモデルを使ってプロジェクトを読み、ファイルを編集し、コマンドを実行して、自分の作業結果を確認するオープンソースのエージェントです。まずはターミナルで一つのタスクから始めましょう。大きな仕事では、異なるモデルや役割を持つエージェントに作業の一部を分担させられます。 -![ターミナルで動作する Codewhale](web/public/codewhale-tui-d7a9a1c.png) +![ターミナルで動作する Codewhale](web/public/codewhale-tui-5765d80.png) *v0.10.0 の開発ビルドによるターミナルのプレビュー。* diff --git a/README.ko-KR.md b/README.ko-KR.md index d8f8cbdfc1..faa4215d5f 100644 --- a/README.ko-KR.md +++ b/README.ko-KR.md @@ -1,9 +1,9 @@ - + # Codewhale Codewhale은 사용자가 선택한 호스팅 모델이나 로컬 모델로 프로젝트를 읽고, 파일을 편집하고, 명령을 실행하며, 작업 결과를 확인하는 오픈 소스 에이전트입니다. 터미널에서 하나의 작업으로 시작하세요. 더 큰 작업은 서로 다른 모델과 역할을 가진 에이전트에게 나누어 맡길 수 있습니다. -![터미널에서 실행 중인 Codewhale](web/public/codewhale-tui-d7a9a1c.png) +![터미널에서 실행 중인 Codewhale](web/public/codewhale-tui-5765d80.png) *v0.10.0 개발 빌드의 터미널 미리보기입니다.* diff --git a/README.md b/README.md index a6ee3fbfcf..1fc6844f79 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,8 @@ agents with different models and roles. [![Discord](https://img.shields.io/badge/Discord-join-5865F2?logo=discord&logoColor=white)](https://discord.gg/37gfS3ksug) - - A Codewhale terminal session + + A Codewhale terminal session *Terminal preview from a v0.10.0 development build.* diff --git a/README.pl.md b/README.pl.md index 536c468d42..fc0e3fef50 100644 --- a/README.pl.md +++ b/README.pl.md @@ -1,9 +1,9 @@ - + # Codewhale Codewhale to agent o otwartym kodzie źródłowym, który czyta Twój projekt, edytuje pliki, wykonuje polecenia i sprawdza swoją pracę przy użyciu wybranego przez Ciebie modelu hostowanego lub lokalnego. Zacznij od jednego zadania w terminalu. Przy większej pracy powierz jej części agentom korzystającym z różnych modeli i pełniącym różne role. -![Codewhale działający w terminalu](web/public/codewhale-tui-d7a9a1c.png) +![Codewhale działający w terminalu](web/public/codewhale-tui-5765d80.png) *Podgląd terminala z rozwojowej kompilacji v0.10.0.* diff --git a/README.pt-BR.md b/README.pt-BR.md index 6a3db4191a..e0bd3cc2c8 100644 --- a/README.pt-BR.md +++ b/README.pt-BR.md @@ -1,9 +1,9 @@ - + # Codewhale Codewhale é um agente de código aberto que lê seu projeto, edita arquivos, executa comandos e verifica o próprio trabalho usando um modelo hospedado ou local à sua escolha. Comece com uma tarefa no terminal. Para um trabalho maior, distribua partes do trabalho entre agentes com diferentes modelos e funções. -![Codewhale em execução em um terminal](web/public/codewhale-tui-d7a9a1c.png) +![Codewhale em execução em um terminal](web/public/codewhale-tui-5765d80.png) *Prévia do terminal em uma build de desenvolvimento da v0.10.0.* diff --git a/README.ru.md b/README.ru.md index 739652a002..bbd22cc7d0 100644 --- a/README.ru.md +++ b/README.ru.md @@ -1,9 +1,9 @@ - + # Codewhale Codewhale — агент с открытым исходным кодом, который читает ваш проект, редактирует файлы, выполняет команды и проверяет свою работу с помощью выбранной вами облачной или локальной модели. Начните с одной задачи в терминале. Для большой работы поручайте её части агентам с разными моделями и ролями. -![Codewhale работает в терминале](web/public/codewhale-tui-d7a9a1c.png) +![Codewhale работает в терминале](web/public/codewhale-tui-5765d80.png) *Предварительный вид терминала из сборки v0.10.0, находившейся в разработке.* diff --git a/README.tr.md b/README.tr.md index 772a36407f..67a63f717f 100644 --- a/README.tr.md +++ b/README.tr.md @@ -1,9 +1,9 @@ - + # Codewhale Codewhale, seçtiğiniz barındırılan veya yerel bir modeli kullanarak projenizi okuyan, dosyaları düzenleyen, komutları çalıştıran ve yaptığı işi kontrol eden açık kaynaklı bir ajandır. Terminalde tek bir görevle başlayın. Daha büyük bir işte, işin bölümlerini farklı model ve rollere sahip ajanlara verin. -![Terminalde çalışan Codewhale](web/public/codewhale-tui-d7a9a1c.png) +![Terminalde çalışan Codewhale](web/public/codewhale-tui-5765d80.png) *v0.10.0 geliştirme derlemesinden terminal önizlemesi.* diff --git a/README.uk.md b/README.uk.md index 8828068fc5..76bea78d98 100644 --- a/README.uk.md +++ b/README.uk.md @@ -1,9 +1,9 @@ - + # Codewhale Codewhale — агент із відкритим кодом, який читає ваш проєкт, редагує файли, виконує команди й перевіряє свою роботу за допомогою обраної вами хмарної або локальної моделі. Почніть з одного завдання в терміналі. Для великої роботи доручайте її частини агентам із різними моделями й ролями. -![Codewhale працює в терміналі](web/public/codewhale-tui-d7a9a1c.png) +![Codewhale працює в терміналі](web/public/codewhale-tui-5765d80.png) *Попередній вигляд термінала зі збірки v0.10.0, що перебувала в розробці.* diff --git a/README.vi.md b/README.vi.md index 7aecaf5e06..25f1ab2095 100644 --- a/README.vi.md +++ b/README.vi.md @@ -1,9 +1,9 @@ - + # Codewhale Codewhale là tác nhân mã nguồn mở có thể đọc dự án, chỉnh sửa tệp, chạy lệnh và kiểm tra công việc của mình bằng mô hình do nhà cung cấp lưu trữ hoặc mô hình cục bộ mà bạn chọn. Hãy bắt đầu với một tác vụ trong terminal. Với công việc lớn hơn, bạn có thể giao từng phần cho các tác nhân dùng mô hình và đảm nhiệm vai trò khác nhau. -![Codewhale đang chạy trong terminal](web/public/codewhale-tui-d7a9a1c.png) +![Codewhale đang chạy trong terminal](web/public/codewhale-tui-5765d80.png) *Hình xem trước terminal từ bản dựng phát triển v0.10.0.* diff --git a/README.zh-CN.md b/README.zh-CN.md index 50ef9ad257..2c752ec3bb 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -1,9 +1,9 @@ - + # Codewhale Codewhale 是一款开源智能体,可使用你选择的托管模型或本地模型读取项目、编辑文件、运行命令并检查自己的工作。从终端中的一项任务开始。对于较大的工作,可以将其中的部分任务交给使用不同模型、承担不同角色的智能体。 -![Codewhale 在终端中运行](web/public/codewhale-tui-d7a9a1c.png) +![Codewhale 在终端中运行](web/public/codewhale-tui-5765d80.png) *终端预览截图来自 v0.10.0 的开发构建。* diff --git a/README.zh-TW.md b/README.zh-TW.md index b0e2425e61..5ac2a0ca6c 100644 --- a/README.zh-TW.md +++ b/README.zh-TW.md @@ -1,9 +1,9 @@ - + # Codewhale Codewhale 是一款開源代理,可使用你選擇的託管模型或本機模型讀取專案、編輯檔案、執行指令,並檢查自己的工作。從終端機中的一項任務開始。對於較大的工作,可以將部分任務交給使用不同模型、擔任不同角色的代理。 -![Codewhale 在終端機中執行](web/public/codewhale-tui-d7a9a1c.png) +![Codewhale 在終端機中執行](web/public/codewhale-tui-5765d80.png) *終端機預覽截圖來自 v0.10.0 的開發建置版本。* diff --git a/crates/tui/tests/cucumber/launch_card_pty.rs b/crates/tui/tests/cucumber/launch_card_pty.rs index 8aa83fbd97..e28e2603e0 100644 --- a/crates/tui/tests/cucumber/launch_card_pty.rs +++ b/crates/tui/tests/cucumber/launch_card_pty.rs @@ -4,7 +4,9 @@ use std::time::Duration; use super::qa_harness; -use qa_harness::harness::{Harness, SealedWorkspace, make_sealed_workspace}; +use qa_harness::harness::{ + Harness, SealedWorkspace, make_sealed_workspace, make_sealed_workspace_in_home, +}; use qa_harness::keys; const WAIT: Duration = Duration::from_secs(15); @@ -12,19 +14,65 @@ const SIZES: [(u16, u16); 5] = [(12, 40), (16, 60), (24, 80), (32, 100), (40, 14 const TITLE: &str = "Recent proof"; const SAVED_TEXT: &str = "Restored conversation proof"; +/// Published codewhale.net terminal media. Default Underwater theme, no +/// motion, a home-relative workspace, and only states a fresh install really +/// reaches: the first-run provider picker, home, a typed draft, and /help. #[test] #[ignore = "opt-in website media; empty isolated session, no provider calls"] fn website_current_terminal_capture() { assert!(std::env::var_os("QA_LAUNCH_CAPTURE_DIR").is_some()); - let (_workspace, mut tui) = - start_with_options(24, 100, false, &[], Some("shoreline"), true, false); + let workspace = make_sealed_workspace_in_home("my-project").unwrap(); + let (_workspace, mut tui) = start_in( + workspace, + Launch { + rows: 24, + cols: 100, + // The fixture Ctrl+U leaves a "Ctrl+Z restores" receipt that + // never expires on an idle, motionless screen. + keep_composer: true, + ..Launch::default() + }, + |tui| capture(tui, "website-provider-picker"), + ); // Capture only actual application output: no fabricated history, usage, // connected tools, model response or completed work. - tui.wait_for_idle(Duration::from_secs(4), WAIT).unwrap(); + tui.wait_for_idle(Duration::from_secs(1), WAIT).unwrap(); + assert_website_frame(&mut tui); capture(&mut tui, "website-home"); + + // Backspace, not Ctrl+U: clearing by Ctrl+U raises a receipt that would + // be published with the frame. + let prompt = "Add a --json flag to the export command"; + tui.send(keys::key::backspaces(80)).unwrap(); + wait(&mut tui, "Type a message"); + tui.send(prompt).unwrap(); + wait(&mut tui, prompt); + wait(&mut tui, "[↵]"); + assert_website_frame(&mut tui); + capture(&mut tui, "website-composer"); + tui.send(keys::key::backspaces(prompt.len())).unwrap(); + wait(&mut tui, "Type a message"); + + tui.paste("/help").unwrap(); + tui.wait_for_idle(Duration::from_millis(300), WAIT).unwrap(); + tui.send(keys::key::enter()).unwrap(); + wait(&mut tui, "/model"); + capture(&mut tui, "website-help"); tui.shutdown(); } +/// The published frame names the project the way a user's shell would and +/// never leaks the sealed tempdir. +fn assert_website_frame(tui: &mut Harness) { + tui.pump(); + let text = tui.frame().text(); + assert!( + text.contains("~/my-project") && !text.contains(".tmp"), + "website frame must show ~/my-project and no tempdir: {}", + tui.diagnostics() + ); +} + fn start(rows: u16, cols: u16, with_mcp: bool) -> (SealedWorkspace, Harness) { start_titled(rows, cols, with_mcp, TITLE) } @@ -61,7 +109,52 @@ fn start_with_options( animated: bool, no_color: bool, ) -> (SealedWorkspace, Harness) { - let workspace = make_sealed_workspace().unwrap(); + start_in( + make_sealed_workspace().unwrap(), + Launch { + rows, + cols, + with_mcp, + titles, + theme, + animated, + no_color, + keep_composer: false, + }, + |_| {}, + ) +} + +#[derive(Default)] +struct Launch<'a> { + rows: u16, + cols: u16, + with_mcp: bool, + titles: &'a [&'a str], + theme: Option<&'a str>, + animated: bool, + no_color: bool, + /// Skip the post-launch Ctrl+U that empties the composer. + keep_composer: bool, +} + +/// Launch into `workspace` and walk first-run onboarding to home. +/// `on_provider_picker` sees the real first-run provider picker. +fn start_in( + workspace: SealedWorkspace, + launch: Launch<'_>, + on_provider_picker: impl FnOnce(&mut Harness), +) -> (SealedWorkspace, Harness) { + let Launch { + rows, + cols, + with_mcp, + titles, + theme, + animated, + no_color, + keep_composer, + } = launch; let trust = workspace.workspace().join(".deepseek"); let sessions = workspace.home().join(".codewhale/sessions"); for directory in [&trust, &sessions] { @@ -123,6 +216,7 @@ fn start_with_options( .seal_home(workspace.home()) .env("CODEWHALE_DISABLE_MODELS_DEV_FETCH", "1") .env("CODEWHALE_NO_UPDATE_CHECK", "1") + .env("CODEWHALE_DISABLE_LOCAL_OLLAMA_PROBE", "1") .env("NO_ANIMATIONS", if animated { "0" } else { "1" }) .env("COLORTERM", "truecolor") .env("NO_COLOR", if no_color { "1" } else { "" }) @@ -137,11 +231,12 @@ fn start_with_options( .spawn() .unwrap(); wait(&mut tui, "Choose your model provider"); + on_provider_picker(&mut tui); tui.send(keys::key::ctrl('o')).unwrap(); wait(&mut tui, "You're ready."); tui.send(keys::key::enter()).unwrap(); wait(&mut tui, "New session"); - if animated { + if animated || keep_composer { return (workspace, tui); } tui.wait_for_idle(Duration::from_millis(300), WAIT).unwrap(); diff --git a/crates/tui/tests/support/qa_harness/harness.rs b/crates/tui/tests/support/qa_harness/harness.rs index fe971922e2..1ac61f71a9 100644 --- a/crates/tui/tests/support/qa_harness/harness.rs +++ b/crates/tui/tests/support/qa_harness/harness.rs @@ -608,9 +608,20 @@ fn consume_cursor_position_queries(tail: &mut Vec, bytes: &[u8]) -> usize { /// Construct a sealed-`HOME` workspace under a `tempfile::TempDir` so the /// scenario can never read or mutate the developer's real config / skills. pub fn make_sealed_workspace() -> Result { + sealed_workspace_at(|tmp, _home| tmp.join("workspace")) +} + +/// Like [`make_sealed_workspace`], but the workspace is `HOME/`, so +/// the TUI displays it as `~/` the way a real user's checkout reads +/// instead of an absolute tempdir path. Used for published website captures. +pub fn make_sealed_workspace_in_home(project: &str) -> Result { + sealed_workspace_at(|_tmp, home| home.join(project)) +} + +fn sealed_workspace_at(workspace: impl FnOnce(&Path, &Path) -> PathBuf) -> Result { let tmp = tempfile::TempDir::new().context("tempdir")?; - let workspace = tmp.path().join("workspace"); let home = tmp.path().join("home"); + let workspace = workspace(tmp.path(), &home); std::fs::create_dir_all(&workspace).context("mkdir workspace")?; std::fs::create_dir_all(home.join(".codewhale")).context("mkdir home/.codewhale")?; std::fs::create_dir_all(home.join(".deepseek")).context("mkdir home/.deepseek")?; diff --git a/docs/public-surface-facts.json b/docs/public-surface-facts.json index c31cd93276..d4604cf4e2 100644 --- a/docs/public-surface-facts.json +++ b/docs/public-surface-facts.json @@ -248,17 +248,19 @@ ] }, "screenshot": { - "readme": "web/public/codewhale-tui-d7a9a1c.png", - "website": "web/public/codewhale-tui-d7a9a1c.png", + "readme": "web/public/codewhale-tui-5765d80.png", + "website": "web/public/codewhale-tui-5765d80.png", "sourceVersion": "0.10.0", - "sourceCommit": "d7a9a1c8c15635a84099d5fc2ee965146cf197c7", - "sourceSha256": "73e56158d6ec6c04933ce787c23c07322ce4d509b54050b54b12d181b09314b8", - "terminal": "real PTY cell capture rendered with Menlo and Apple Symbols", - "theme": "Shoreline dark", - "capture": "Captured on 2026-09-20 from the exact d7a9a1c8c156 development build in an isolated empty workspace: canonical whale mark, new session, composer, Ask permissions, Work mode and model status. Rendered directly from terminal cells without changing the content. No fixture conversations or provider requests. This documents appearance, not a published release or completed task. Ask/Work is the captured posture, not a default claim.", + "sourceCommit": "5765d80278f7184d187fa6682ba96b403a006523", + "sourceSha256": "359359275fefed015ba0e3772fee9e09d64048813d311c31d6ee2a83b1ee85b5", + "terminal": "real PTY cell capture; the website renders the cells as live HTML text, and the README image is the same frame rasterized with Menlo by web/scripts/render-terminal-capture.mjs", + "theme": "Underwater (default)", + "capture": "Captured on 2026-09-25 from a development build of source 5765d80278f7 (0.10.0 dev) by crates/tui/tests/cucumber/launch_card_pty.rs::website_current_terminal_capture: a sealed HOME with the workspace at ~/my-project, no provider connected (explored offline), no network probes, no animations. Shows the whale mark, the first-run starter prompt in the composer, Ask permissions, Work mode and the model status. Rendered directly from terminal cells without changing the content. No fixture conversations or provider requests. This documents appearance, not a published release or completed task. Ask/Work is the captured posture, not a default claim.", "sources": [ "crates/tui/tests/cucumber/launch_card_pty.rs", - "web/public/codewhale-tui-d7a9a1c.png" + "web/lib/terminal-captures/website-home-100x24.json", + "web/scripts/render-terminal-capture.mjs", + "web/public/codewhale-tui-5765d80.png" ] } } diff --git a/web/app/[locale]/constitution/page.tsx b/web/app/[locale]/constitution/page.tsx index 8d6cd0ffc7..6898f18140 100644 --- a/web/app/[locale]/constitution/page.tsx +++ b/web/app/[locale]/constitution/page.tsx @@ -54,8 +54,11 @@ export default async function ConstitutionPage({ params }: { params: Promise<{ l return ( <>
diff --git a/web/app/[locale]/faq/page.tsx b/web/app/[locale]/faq/page.tsx index 9d6bf63f06..5be2eb6831 100644 --- a/web/app/[locale]/faq/page.tsx +++ b/web/app/[locale]/faq/page.tsx @@ -763,8 +763,11 @@ export default async function FaqPage({ params }: { params: Promise<{ locale: st dangerouslySetInnerHTML={{ __html: serializeJsonLd(jsonLd) }} /> diff --git a/web/app/[locale]/feed/page.tsx b/web/app/[locale]/feed/page.tsx index 4f0b1db1fb..be2c8d62a7 100644 --- a/web/app/[locale]/feed/page.tsx +++ b/web/app/[locale]/feed/page.tsx @@ -68,6 +68,7 @@ export default async function FeedPage({ params }: { params: Promise<{ locale: s const copy = isZh ? { title: "动态", + titleAside: "Activity", lede: ( <> 来自{" "} @@ -82,6 +83,7 @@ export default async function FeedPage({ params }: { params: Promise<{ locale: s } : { title: "Activity", + titleAside: "动态", lede: ( <> Follow issues and pull requests from{" "} @@ -106,7 +108,14 @@ export default async function FeedPage({ params }: { params: Promise<{ locale: s return ( <> - +
{columns.map((column) => ( diff --git a/web/app/[locale]/models/page.tsx b/web/app/[locale]/models/page.tsx index 75490e10e2..1c3ad8654c 100644 --- a/web/app/[locale]/models/page.tsx +++ b/web/app/[locale]/models/page.tsx @@ -2,7 +2,7 @@ import Link from "next/link"; import { getFacts } from "@/lib/facts"; import { buildPageMetadata } from "@/lib/page-meta"; import { MODELS_COPY } from "@/lib/content/models"; -import { Icon, type IconName } from "@/components/icon"; +import { Icon } from "@/components/icon"; import { ModelsTable } from "@/components/models-table"; import { PageHeader, Section } from "@/components/page-header"; import type { LocalizedText } from "@/lib/content/vocabulary"; @@ -17,8 +17,6 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s description: pickText(MODELS_COPY.metaDescription, locale) }); } -const PATTERN_ICONS: IconName[] = ["key", "cpu", "layers"]; - /** * /models — connect a provider, then the source's own model and provider * lists (facts layer), never a hand-typed catalogue. @@ -53,22 +51,23 @@ export default async function ModelsPage({ params }: { params: Promise<{ locale: />
-
-
    - {MODELS_COPY.patterns.map((pattern, index) => ( -
  • - - - - {t(pattern.title)} - {t(pattern.detail)} - {pattern.reference} - - - -
  • +
    +
    + {MODELS_COPY.patterns.map((pattern) => ( +
    +
    {pattern.reference}
    +
    + {t(pattern.title)} · {t(pattern.detail)} +
    +
    ))} -
+
diff --git a/web/app/[locale]/page.tsx b/web/app/[locale]/page.tsx index 4c3eaee419..5faa2090c5 100644 --- a/web/app/[locale]/page.tsx +++ b/web/app/[locale]/page.tsx @@ -1,11 +1,13 @@ import { Fragment } from "react"; -import Image from "next/image"; import Link from "next/link"; import { GettingStartedSteps } from "@/components/getting-started-steps"; import { HeroInstall } from "@/components/hero-install"; import { Icon, type IconName } from "@/components/icon"; import { InstallCodeBlock } from "@/components/install-code-block"; +import { Section } from "@/components/page-header"; import { Status, type StatusTone } from "@/components/status-badge"; +import { Strata } from "@/components/strata"; +import { TerminalCapture } from "@/components/terminal-capture"; import { WhalePose } from "@/components/whale-pose"; import { getFacts } from "@/lib/facts"; import { GETTING_STARTED_STEPS } from "@/lib/content/getting-started"; @@ -25,22 +27,23 @@ import { TERMINAL_SCREENSHOT } from "@/lib/media-manifest"; // caching. `getFacts()` rejects legacy or older KV snapshots. export const revalidate = 300; -// Row order is shared by every locale's `gain`, `surfaces` and -// `availability` lists, so the marks and states follow the row, not a word. +// Row order is shared by every locale's `gain` and `availability` lists, so +// the marks and states follow the row, not a word. const GAIN_ICONS: IconName[] = ["terminal", "repeat", "shield"]; -const SURFACE_ICONS: IconName[] = ["terminal", "plug", "monitor", "folder", "users"]; // Released · development preview · development build · in development. const AVAILABILITY_TONES: StatusTone[] = ["ready", "attention", "idle", "idle"]; /** - * The whale-road homepage: the promise and the install plate in the sky, - * the whale resting on one calm horizon, and everything else in the sea - * below it, which continues into the footer. + * The whale-road homepage: the promise and the install plate in the sky over + * the rising water, the whale resting on one calm horizon, and everything + * else in the sea below it. Chapters read as an editorial folio: a numbered + * running head, a title at section scale, and a ruled list beside it. One + * waterline band takes the page from the shallows into deep water. * * Every visible string resolves through `getHome(locale)`. The only literals * left here are code-owned per docs/VOICE.md: the product control vocabulary * (`Plan · Work · Operate`, `Ask · Auto-Review · Full Access`), package - * channel proper nouns, and the screenshot path. + * channel proper nouns, and the chapter numerals. */ export default async function HomePage({ params }: { params: Promise<{ locale: string }> }) { const { locale } = await params; @@ -68,9 +71,12 @@ export default async function HomePage({ params }: { params: Promise<{ locale: s /> {/* THE SKY — the promise, the one primary action, the install plate, - and the whale resting on the horizon. */} + and the whale resting on the horizon over the rising water. */}
+

{d.heroTitle}

@@ -103,65 +109,63 @@ export default async function HomePage({ params }: { params: Promise<{ locale: s
- {/* THE SEA — deep water in dark, continuing into the footer; shallow - water settling back to paper in light. */} -
+ {/* THE SEA — the shallows under the horizon, then one waterline band + into deep water that continues into the footer. */} +
- {/* YOUR MODELS */} -
-
-
-

{d.modelsHeading}

-

{d.modelsBody}

- - {d.modelsLink} - - -
-
- {d.modelsFacts.map(([kind, description]) => ( -
-
{kind}
-
{description}
-
- ))} -
-
Plan · Work · Operate
-
Ask · Auto-Review · Full Access
+ {/* 02 — YOUR MODELS */} +
+ {d.modelsLink} + + + } + > +
+ {d.modelsFacts.map(([kind, description]) => ( +
+
{kind}
+
{description}
-
-
-
- - {/* START */} -
-
-
-

{d.startHeading}

-

{d.startLede}

+ ))} +
+
Plan · Work · Operate
+
Ask · Auto-Review · Full Access
-
+ +
+ + {/* 03 — START */} +
@@ -244,19 +246,36 @@ export default async function HomePage({ params }: { params: Promise<{ locale: s
-
+
+ - {/* WHERE IT RUNS TODAY — each surface with a mark and a word. */} -
-
-
-

{d.availabilityHeading}

-

{d.availabilityLede}

-
-
-
+ {/* THE WATERLINE — the shallows give way to deep water, which + continues through the footer. */} + + + +
+
+ {/* 04 — WHERE IT RUNS TODAY — each surface with a mark and a word. */} +
+ {d.accountLink} + + + } + > +
{d.availability.map(([surface, status, detail], index) => ( -
+
{surface}
{status} @@ -266,13 +285,7 @@ export default async function HomePage({ params }: { params: Promise<{ locale: s ))}

{d.availabilityNote}

- -
+
{/* INSTALL — the command again, where the page ends. */}
@@ -295,9 +308,9 @@ export default async function HomePage({ params }: { params: Promise<{ locale: s
- {/* COMMUNITY */} + {/* COMMUNITY — a list that needs its marks keeps the boxed rows. */}
-
+

{d.communityHeading}

{d.communityBody}

diff --git a/web/app/[locale]/product/page.tsx b/web/app/[locale]/product/page.tsx index 41ac3b41a8..0b19378bfc 100644 --- a/web/app/[locale]/product/page.tsx +++ b/web/app/[locale]/product/page.tsx @@ -22,7 +22,6 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s // Row order is fixed by PRODUCT_COPY and the home dictionary, so marks and // states follow the row, not a translated word. const GAIN_ICONS: IconName[] = ["layers", "users", "shield"]; -const SURFACE_ICONS: IconName[] = ["terminal", "plug", "monitor", "folder", "users"]; // Terminal released · local browser ships with it · hosted web preview · // desktop development build · cloud computers in development. const AVAILABILITY_TONES: StatusTone[] = ["ready", "ready", "attention", "idle", "idle"]; @@ -69,10 +68,10 @@ export default async function ProductPage({ params }: { params: Promise<{ locale } > -
+
{PRODUCT_COPY.gain.map((row, index) => ( -
-