Imported from yaffalhakim1/kaku-gui (
AGENTS.md). Install upstream withnpx skills add yaffalhakim1/kaku-gui. Copyright stays with the author.
Kaku GUI — Session Context
Learner profile
- Background: experienced web developer (frontend + backend).
- Rust knowledge: 0.
- Desktop / GPU UI knowledge: 0.
- Learning style: learn by doing. Wants to type the code themselves.
- Preferred pace: one small phase per session, like an 8-hour tutorial split into episodes.
Project goal
Build a native GUI client for OpenCode using Rust + GPUI (https://gpui-kit.com/) (same stack as waku C:\Users\yafit\Documents\Learn\rust\waku).
This is a personal learning project, not production code.
Workflow (MANDATORY)
Session pacing rule (added 2026-09-22): when a phase would introduce more than ~4 new Rust concepts in one sitting, split it first (see the Phase splitting convention below) and teach one part per sitting. The user found the original 365-line Phase 03 too much for one sitting.
- The project is split into phases under
docs/phases/(large phases are split further into numbered parts — see the Phase splitting convention). - Each session, the user picks ONE phase (or one part) by saying something like:
- "read phase 01"
- "let's do phase 03b"
- "explain phase 00"
- The assistant MUST:
- Read the requested phase markdown.
- Explain the phase goal and Rust / GPUI concepts.
- Provide code blocks for the user to type themselves.
- NOT write the code into files unless explicitly asked.
- The user does the heavy lifting: typing, compiling, and debugging.
- The assistant acts like a tutorial narrator + debugger.
Phase roadmap
This section is the single source of truth for the roadmap. If docs/implementation_plan.md or task.md disagrees with it, this section wins — those files are historical notes.
- Phase 00: Recap the minimal scaffold (3 files: main.rs, app.rs, theme.rs).
- Phase 01: Static chat layout.
- Phase 02: Text input + submit on Enter.
- Phase 03: Connect to OpenCode (health + session) — split into 03a/03b/03c (see below).
- Phase 04: Send prompts.
- Phase 05: SSE streaming — split into 05a/05b/05c (see below).
- Phase 06: Abort, scroll, status polish — split into 06a/06b (see below).
- Phase 07: Slash commands (/clear, /model, /quit).
- Phase 08: Waku-inspired UI/UX polish (
docs/phases/08-waku-uiux.md). - Markdown / reasoning rendering is OUT OF SCOPE until Phase 07 is done. It is not Phase 08.
The phase files that exist today are 00-scaffold.md through 08-waku-uiux.md, plus the split parts for Phases 03, 05, and 06. Phase 00 is a recap of a scaffold that already exists in src/; do not re-create it.
Phase splitting convention (introduced 2026-09-22)
Large phases are split into numbered parts (03a-*.md, 03b-*.md, ...) when
one sitting would introduce more than ~4 new Rust concepts. Rules:
- The original file becomes a pointer stub listing the parts in order.
- Each part is self-contained: it compiles on its own with 0 errors and ends with its own verify step.
- Each part carries a status header at the top (
completed/in progresswith what remains /not started). A fresh session reads the header first. task.mdrecords the split and the current position.- Phases already split: 03 (03a client-types, 03b startup-connect, 03c status-bar), 05 (05a stream-plumbing, 05b read-stream, 05c stream-e2e), and 06 (06a abort, 06b scroll-status).
Progress through the roadmap lives in task.md. This file does not track
which phases are done or which source files exist; check task.md for both so
the two cannot drift apart.
Stack facts (verified — do not guess)
-
GPUI comes from a fork.
gpuiandgpui_platformare bothgit = "https://github.com/egoist/zed", branch = "waku-webview". These are not crates.io dependencies. gpui reports version0.2.2. -
The fork is Zed upstream
mainplus PR #61945 (layered scene rendering), which lets GPUI composite menus and tooltips above native child views. Reasoning: drop back to upstream once that PR merges. -
gpui_platformis required forapplication(). It is a separate crate fromgpui; you cannot open a window withgpuialone. -
kaku-gui is pinned to the same fork as waku. kaku-gui is Windows-first, so the fork's macOS WebView concerns are irrelevant here.
-
Toolchain: stable Rust (MSVC target on Windows). If you see an error about
cold_path, the toolchain is too old —rustup update stable. -
Cargo.tomlprofiles are deliberate:[profile.dev] opt-level = 1and[profile.dev.package."*"] opt-level = 2keep GPUI's text shaping and layout hot paths from running fully unoptimized in debug builds. -
Dependencies are fixed to an approved set. No new dependency without asking first. The approved set is:
Crate Why gpuithe UI framework gpui_platformapplication()lives here, not ingpuianyhowerror propagation reqwest_clientthe working HttpClientimpl (same fork/branch)serde(derive)#[derive(Deserialize)]serde_jsonJSON parsing futuresAsyncReadExt/AsyncBufReadExtto read response bodiesDependency fact (corrected 2026-09-22): six of the seven (
gpui,gpui_platform,anyhow,serde,serde_json,futures) ARE already inCargo.lockviagpui.reqwest_clientis NOT — adding it brings roughly 78 new crates (tokio, zed-reqwest, hyper, h2, rustls, tower) and the firstcargo checktakes about 1.5 minutes. It is still the only workingHttpClientimpl in the fork, so it remains the right choice. Anything outside this set still needs to be asked for.
GPUI API rules (MANDATORY)
-
Never write a GPUI API from memory. GPUI has almost no public docs and churns constantly. Every GPUI symbol you quote must come from the pinned checkout on disk.
-
The pinned checkout is at:
C:\Users\yafit\.cargo\git\checkouts\zed-4d64e9894aeee3ad\57bd4fe→crates/gpui/,crates/gpui_platform/,crates/http_client/,crates/reqwest_client/. That rev (57bd4fe181639797d395978d5de17bc9e10a6219) is what kaku-gui'sCargo.lockresolves to.f9bad89is waku's rev, not ours — both checkouts sit side by side on disk, so pointing at the wrong one fails silently. Confirm the rev againstCargo.lockbefore reading. -
The best examples are inside that checkout's
crates/gpui/examples/: readinput.rs(text input,EntityInputHandler),list_example.rsanduniform_list.rs(virtualized lists),scrollable.rs(scroll handles),animation.rs,popover.rs. -
https://gpui-kit.com/is a reading reference only. It is not a dependency and its snippets may target a different GPUI revision. Verify anything you take from it against the pinned checkout before teaching it. -
Zed's own
ui,theme, andcomponentcrates exist in the same fork, but kaku-gui does not use them. All UI is hand-rolled withdiv(). Do not propose adding them. -
Useful confirmed API facts (still verify before quoting):
div().id("...")returnsStateful<E>;.hover(),.active(),.focus(),.on_click(),.on_hover()live onStatefulInteractiveElement/InteractiveElement, reachable viagpui::prelude::*.cx.background_executor()andcx.spawn()onApp/Context<T>.
-
async in GPUI is smol, not tokio. Concretely:
- Use
cx.spawn(...),cx.background_executor().spawn(...), andTask<T>.Taskis cancelled when dropped; call.detach()to let it run. - The async-closure form is the one that compiles:
cx.spawn(async move |this, cx| { ... }). The two-argument formcx.spawn(|this, mut cx| async move { ... })fails withE0282: type annotations needed. Do not teach the second form. - Results come back into the UI through a channel that
renderdrains — the UI must never block on a future. - Do not add a tokio runtime to the app.
ReqwestClientowns one internally; the app never constructs one and never calls into it directly. - Do not teach any of this before its phase. It is recorded here so the assistant plans correct phases, not so the user learns it early.
- Use
-
HTTP:
cx.http_client()is not enough on its own. This is the trap that wastes an afternoon:gpui_platform::application()callsApplication::with_platform, which installsNullHttpClient. Every request through it fails at runtime withNo HttpClient available— there is no compile error.- The app must install a real client at startup:
application().with_http_client(Arc::new(ReqwestClient::new())).run(...). - After that,
cx.http_client()(onApp, and onContext<T>viaDeref) returns the working client. - Request/response types come from
gpui::http_client:AsyncBody,HttpClient,Method,Request,Url,Json.get(uri, body, follow_redirects)andpost_json(uri, body)returnResponse<AsyncBody>. - Read bodies with
futures::AsyncReadExt::read_to_string, or stream lines withfutures::AsyncBufReadExtoverfutures::io::BufReader.
-
The re-export trap.
gpuire-exportsserde,serde_json,anyhow,http_client, andUrl, which tempts you into thinking no dependency is needed. Two things still need a direct dependency:#[derive(Deserialize)]fails withE0463: can't find crate for serdeunlessserdeis a direct dependency. The re-export does not satisfy the derive macro.futuresis not re-exported bygpuiat all, and reading a body needsAsyncReadExtfrom it.
Teaching style
- Keep explanations short and concrete.
- Include "Rust concept" callouts inside each phase (e.g., what
&mut selfmeans). - Prefer web-dev analogies:
- GPUI
Entity<T>≈ React component instance Rendertrait ≈ Reactrender()methodContext<T>≈ React context + setState combineddiv().flex()≈ CSS flexbox
- GPUI
- Never assume the user knows Rust syntax, ownership, lifetimes, or traits.
- Define every Rust word the first time it appears
- Show the JS equivalent next to it when one exists
- Assume you’ve never seen the syntax, so spell out what each symbol does
- Say “this one is new” when a concept hasn’t come up yet
Code style
- Modules are added phase by phase as the user types them. See
task.mdfor which files exist right now. - Follow the existing kaku dark theme palette.
- Keep the scaffold compiling after every phase.
Important constraints
- Do not jump ahead to future phases.
- Do not introduce concepts before their phase.
- Do not auto-fix compile errors for the user unless asked; guide them to the fix instead.
- When the user says "read phase XX", read
docs/phases/XX-*.mdand teach from it.
Tutorial mode vs. the artifact rules
In a tutorial session the assistant writes no code into src/. The master
rules' artifacts (implementation_plan.md, task.md, walkthrough.md) are
narrated in chat during tutorial sessions, not written to disk, unless the user
explicitly asks for a file.
Reference material (read-only, outside this workspace)
These live outside the kaku-gui workspace root, so they may not be readable without asking the user first. Do not assume access.
| Path | What it is for |
|---|---|
C:\Users\yafit\Documents\Learn\rust\waku |
Same GPUI stack. Design reference for Phase 08, and src/ui/ shows hand-rolled widgets in this exact fork. |
Also worth knowing: waku's Cargo.toml documents why the fork exists (see the
comment above its gpui dependency), which is the authority if you ever doubt
the fork-vs-upstream decision.
