Documentation

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.

FlagWhat it means
--appAn 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
A tool gets no 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.

Rust, C, C++, Zig and Python tools take a screen. Go and TypeScript are refused by name, in the same words 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