whitehall init
Start something — an app, or a tool in its own language.
Usage
whitehall init [name] With no flags it asks which door you want and, for a tool, which language. Every question has a flag that skips it, so a script never waits on one.
| Flag | What it means |
|---|---|
--app | An app: .wh, Compose, routes, Gradle. With no name, adds a screen to the tool in the current directory |
--lang <x> | A tool in rust, python, go, ts or zig |
Example
whitehall init myapp --app
cd myapp
whitehall dev A tool
A tool's scaffolding is not Whitehall's. --lang rust runs cargo init, so you get the crate a Rust developer expects — and the same for go mod init, npm init and zig init. Python gets a main.py with a PEP 723 header.
whitehall init tool --lang rust
cd tool
whitehall shell
(whitehall) $ cargo run # builds for the phone, runs there whitehall.toml. It needs one only when it
has something to declare — packages, a service, an icon — and a fresh one does not. That is
what makes whitehall init --lang rust and cargo init produce the
same directory.From a tool to an app
--app with no name, run inside a directory that is already a tool, adds a
screen to it in place:
cd my-crate
whitehall init --app
whitehall build . && whitehall install my-crate Your main.rs is not touched. whitehall.toml gains [screen] — creating the file if there was none — and src/routes/+screen.wh appears: a generated terminal view with the tool's
output in a scrolling text, a field for its arguments, a Run button and a spinner. The
screen calls main in the app's own process, so there is no socket and no
second copy of anything.
import $native.Tool
$onMount {
combine(Tool.output, Tool.errors, Tool.running) { out, err, busy ->
output = out
errors = err
running = busy
}.collect { }
} cargo run in the same directory still runs the tool in a terminal — that is
your loop, not something build produces. From here, moving what main does into pub fns makes them callable from the screen one
at a time, until the terminal view is a screen you wrote.
build uses: their runtime expects to be the
process, and an app already is one. Python is here despite shipping a runtime too,
because a tool directory holds exactly one tool — so its interpreter has exactly one
owner, which is the question the other two leave open.An app: what it creates
A manifest and a route. Everything else — layouts, components, stores, widgets — is a file you add when you want one, and a directory that exists because the convention says where to put it.
myapp/
├── whitehall.toml # Project configuration
├── .gitignore # Git ignore patterns
└── src/
└── routes/
└── +screen.wh # Home screen Generated Files
whitehall.toml
Project configuration with sensible defaults:
name = "myapp"
version = "0.1.0"
package = "com.you.myapp"
[screen]
routes = "src/routes" [screen] is what says this project has a Compose screen, and so that Gradle
is involved. There is no [android] section: it is optional and defaulted, and
a generated file that repeats a default is a value somebody later changes in the wrong
place. Add one when you want to pin min_sdk, name an icon, or say anything
else Android — it is the one place those words live.
src/routes/+screen.wh
The home screen — state, a handler, markup:
var message = "Hello, Whitehall!"
fun handleClick() {
message = "Button clicked!"
}
<Column padding={16} gap={8}>
<Text fontSize={24} fontWeight="bold">
{message}
</Text>
<Button
text="Click Me"
onClick={handleClick}
/>
</Column> src/routes/+layout.wh
Not scaffolded — add one when you want a wrapper around every route:
<Scaffold>
<TopAppBar title="MyApp" />
<slot />
</Scaffold> Next Steps
After creating your project:
1. Run on Device
cd myapp
whitehall dev 2. Edit Files
Open src/routes/+screen.wh and start building your UI.
3. Add Dependencies
whitehall deps add io.coil-kt/coil-compose 4. Watch
whitehall dev is the loop: it watches, rebuilds and reinstalls. To watch the
transpile alone, without a device, use whitehall compile --watch.
Configuration Options
Customize whitehall.toml after creation:
Package Name
Top-level, beside name and version — it is identity, the role a
bundle id plays, and a friend's phone needs it. Not under [android].
package = "com.mycompany.myapp" SDK Versions
Both are defaulted, so a fresh manifest has no [android] section at all.
Write one to pin them:
[android]
min_sdk = 26 # Minimum Android version
target_sdk = 36 # Target Android version Dependencies
[dependencies]
"io.coil-kt/coil-compose" = "2.6.0"
"com.squareup.okhttp3/okhttp" = "4.12.0" App Icons
[android.icons]
default = "static/icon-default.png"
dark = "static/icon-dark.png" Whitehall automatically manages Java, Gradle, and Android SDK.
Run whitehall doctor to verify your setup.
Environment Variables
init writes no .env — a generated file full of example variables
is four lines to delete. Add one when you have something to put in it; Whitehall reads .env, .env.local and the per-build .env.debug/.env.release, SvelteKit's hierarchy.
PUBLIC_API_URL=https://api.example.com Access them in your code:
val apiUrl = $env.PUBLIC_API_URL The PUBLIC_ prefix is SvelteKit's, and it means the same thing: only a
prefixed variable is compiled into the app. Everything else stays on the build machine.
Version Control
The generated .gitignore excludes build artifacts and secrets:
/.whitehall/
/dist/
/build/
/android/
.env.local
.env.*.local
*.apk
*.aab The leading slashes are load-bearing: an unanchored android/ also swallows src/android/, the documented home for hand-written Kotlin, and the first
project to use both lost a file from version control without a word said.
Troubleshooting
Project Already Exists
init writes a new directory and will not write into one that is there — with
one exception, --app with no name, which is the tool-to-app path above. So
choose another name, or remove the directory:
rm -rf myapp
whitehall init myapp --app Permission Denied
Ensure you have write permissions in the current directory:
ls -la . See Also
- whitehall run - Build and launch on device
- whitehall build - Transpile and build APK
- Project Structure - Understanding the file layout