# Chapter 1: Getting Started

This chapter installs Loup, runs a first `.lpweb` program, sets up a project,
and compiles it to WebAssembly. It ends with a look at the browser pipeline
the Language Reference specifies, which is not in this release yet.

## What works today

Loup 1.25.1 runs web-mode code, type-checks it, and compiles it to a
WebAssembly module. It does not ship the browser side yet:

- **Works:** `loup run app.lpweb` runs a `web.compiled` or `web.scripted`
  file on the host interpreter. This is how every runnable listing in this
  book was checked.
- **Works:** `loup-bootstrap check` type-checks a project.
- **Experimental, not audited:** `loup-bootstrap build --target browser`
  writes a single bare `build/<name>.wasm`. It runs under
  `loup-launcher --run-bare`, outside the browser.
- **⚠ Planned:** the browser runtime (`loup-rt.js`), the generated HTML shell,
  `loup serve`, the DOM, reactive UI, Web Components, workers and JavaScript
  interop.

The [book's README](README.md) has the full table.

## Installing Loup

Official builds come from the
[`code-craft-ai-company/loup-releases`](https://github.com/code-craft-ai-company/loup-releases/releases)
GitHub releases. On Linux or macOS:

```sh
curl -fsSL https://github.com/code-craft-ai-company/loup-releases/releases/latest/download/loup-install.sh | sh
```

On Windows, in PowerShell:

```powershell
irm https://github.com/code-craft-ai-company/loup-releases/releases/latest/download/loup-install.ps1 | iex
```

The installer unpacks the toolchain into `~/.loup` (Windows:
`%LOCALAPPDATA%\Programs\Loup`), adds its `bin` directory to your PATH and
needs no administrator rights. To install a specific version, run
`sh -s -- --version 1.25.1` instead of `sh`, or set `LOUP_VERSION` before the
Windows command. The macOS and Windows binaries are not notarized or signed
yet; the installers are unaffected, but if you download an archive in a
browser on macOS, clear the quarantine flag with
`xattr -dr com.apple.quarantine <dir>`.

Open a new terminal and check the installation:

```sh
$ loup --version
loup 1.25.1 (freestanding-capable)
```

Every archive contains three programs this book uses:

- `loup` runs source files (`loup run`).
- `loup-bootstrap` is the compiler that creates projects, type-checks them
  and builds WebAssembly (`init`, `check`, `build`). On macOS and Windows,
  `loup` *is* `loup-bootstrap`; on Linux x86-64, `loup` is the self-hosted
  driver, which does not build Wasm. This book writes `loup-bootstrap` for
  those three commands so they work everywhere.
- `loup-launcher` runs a built `.wasm` module.

## Your first program

Create a file called `hello.lpweb`:

```loup
@mode(web.compiled)

func greeting(name: string): string {
    return "Hello, " + name + "!";
}

func main(): void {
    println(greeting("web"));
    println(greeting("Loup"));
}
```

*Listing 1-1: `hello.lpweb`, a first web.compiled program*

Run it:

```sh
$ loup run hello.lpweb
```

```text
Hello, web!
Hello, Loup!
```

Three things to notice:

- **The first line declares the mode.** `@mode(web.compiled)` must be the
  first thing in the file; only comments may come before it. Every file
  should declare its mode (ruling MODE-2). Web files use the `.lpweb`
  extension (ruling MODE-3).
- **A `web.compiled` file holds declarations.** Statements go inside
  functions. The runner calls `main()` after loading the module (ruling
  ENTRY-1), so that is where the program starts. Chapter 2 explains the
  rules, and how `web.scripted` differs.
- **Signatures are typed.** `greeting` takes a `string` and returns a
  `string`; `main` returns `void`. Inside a function body, `let` and `var`
  infer their types.

### Mistake: an old mode name

Older Loup material writes `@mode(compiled)`. That name was retired in v1.17:

```loup,ignore
@mode(compiled)

func main(): void {
    println("Hello, web!");
}
```

```sh
$ loup run old.lpweb
$
```

Nothing is printed, and the exit status is 0. The compiler does not report
the unknown name; it runs the file as a `host.scripted` script, whose
top-level statements are only declarations, so `main()` is never called.
(This book's tooling refuses retired names, which is why the listing above
is not run.) The fix is the full name, `@mode(web.compiled)`. If a program
prints nothing at all, check the mode line first.

### Mistake: passing the wrong type

Change the second call in Listing 1-1 to pass a number:

```loup,does_not_compile
@mode(web.compiled)

func greeting(name: string): string {
    return "Hello, " + name + "!";
}

func main(): void {
    println(greeting("web"));
    println(greeting(2026));
}
```

```text
main.lpweb:9:22: error[E0001]: argument 1 of 'greeting': expected 'string', found 'int'
```

`loup run` prints one line per error: the file, line and column, the error
code, and the message. Nothing runs, not even the first `println`; the whole
file is checked before any of it executes, and the exit status is 2. Loup
never converts an `int` to a `string` for you. Convert it explicitly with
`str`:

```loup
@mode(web.compiled)

func greeting(name: string): string {
    return "Hello, " + name + "!";
}

func main(): void {
    println(greeting("web"));
    println(greeting(str(2026)));
}
```

```text
Hello, web!
Hello, 2026!
```

## Starting a project

A single file is enough for learning. For an application you want a project:
a directory with a manifest, so the compiler knows where the entry file is
and what to build.

```sh
$ loup-bootstrap init hello-web
created project at …/hello-web
```

This writes:

```text
hello-web/
  hello-web.lppkg
  src/main.loup
  tests/
```

The scaffold is a `host.dual` program in `src/main.loup`, because `.loup` is
the `host.dual` extension. For a web project, rename it to `src/main.lpweb`,
replace its contents with Listing 1-1, and point the manifest at it. The top
of `hello-web.lppkg` then reads:

```toml
name = "hello-web"
version = "0.1.0"
loup = "1.20.0"
authors = []

[build]
entry = "src/main.lpweb"
delivery = "browser"
profile = "release"
```

`entry` names the file that holds `main()`. `delivery = "browser"` makes
`loup-bootstrap build` produce a browser module by default (the key is also
accepted under its older name, `target`). The rest of the scaffold, a set of
empty tables, can stay as it is.

Type-check the project from its directory:

```sh
$ loup-bootstrap check
$
```

No output means no errors. `check` works on a project, not on a lone file,
and prints its diagnostics in a longer form than `loup run`. With the
`greeting(2026)` mistake from above it prints:

```text
error[E0001]: argument 1 of 'greeting': expected 'string', found 'int'
 --> src/main.lpweb:9:22
  |
9 |     println(greeting(2026));
  |                      ^^^^
  |
```

## Building WebAssembly

```sh
$ loup-bootstrap build
Resolving dependencies...
Compiling 1 module(s)...
Generating code...
Build complete: …/hello-web/build/hello-web.wasm (19991 bytes)
…/hello-web/build/hello-web.wasm
```

Because the manifest says `delivery = "browser"`, this is the same as
`loup-bootstrap build --target browser` (`--delivery` is the newer spelling
of the same flag). `web` is not a target name: `--target web` fails with
`unknown delivery shape: web`.

The build writes one file, `build/hello-web.wasm`. There is no `dist/`
directory, no HTML page and no JavaScript loader. To run the module, use the
launcher:

```sh
$ loup-launcher --run-bare build/hello-web.wasm
Hello, web!
Hello, Loup!
```

The launcher is a WebAssembly runtime for your terminal. `--run-bare` runs a
module that has no package manifest beside it; it initializes the module and
then calls `main()`.

Compiled WebAssembly is **experimental and not audited** in Loup 1.25.1, and
some programs that run under `loup run` do not run under the launcher. For
example, the interpolated strings of Chapter 3 (`i"Hello, {$ name $}!"`)
work on the interpreter but stop a Wasm build at run time:

```text
loup: main trap: error while executing at wasm backtrace:
    0:   0x24ff - <module>!main

Caused by:
    call_dynamic: no exported function `RenderContext` for the function value
```

Floor division (`a // b`) and `for await` loops also stop a Wasm build at run
time today. That is why Listing 1-1 joins strings with `+`. The rest of this
book runs its listings with `loup run`.

## The planned browser pipeline

> ⚠ **Planned.** Nothing in this section is in Loup 1.25.1. It describes the
> design in the Language Reference (§1-20.6 browser bootstrap, §2-4 reactive
> UI), and its listings are design sketches that are not checked.

In the specified design, `loup build --target browser` writes a `dist/`
directory with an HTML shell, a versioned JavaScript bootstrap
(`loup-rt.js`), and `app.wasm`, plus compressed copies and, with `--debug`,
a source map. When the page loads, `loup-rt.js` runs these steps in order:

1. instantiate `app.wasm`, streaming it as it downloads;
2. initialize the module (run its top-level declarations);
3. register every `@webcomponent` class as a custom element;
4. wire event handlers (elements marked with `data-loup-ev`);
5. hydrate server-rendered state, if there is any;
6. mount the application.

`loup serve` would serve `dist/`, rebuild on every change and reload the
page. A counter app would be two files: an entry that mounts the root
component, and the component itself.

```loup,ignore
# src/main.lpweb
@mode(web.scripted)

from ui import (mount);
from app import (App);

mount(App(), "#root");
```

````loup,ignore
# src/app.lpweb
@mode(web.compiled)

from ui import (signal, View);

pub func App(): View {
    let count = signal(0);
    return ```(html):(none)
    <main>
      <h1>Hello from Loup</h1>
      <p>Count: {$ count.get() $}</p>
      <button onclick={$ func() { count.set(count.get() + 1); } $}>+1</button>
    </main>
    ```;
}
````

*Listing 1-2: Design sketch of a planned counter app*

`signal(0)` creates a reactive value. Reading it inside the fence makes the
paragraph depend on it, so `set` re-renders only that text. The `onclick`
value is a Loup closure, not a JavaScript string; the compiler gives it an
event slot, and the bootstrap attaches it with `addEventListener`. Chapter 16
covers the reactive model and Chapter 19 the build output and deployment.

## Summary

- Install Loup from the official `loup-releases` installers.
- A web file has the `.lpweb` extension and starts with
  `@mode(web.compiled)` or `@mode(web.scripted)`. A retired name such as
  `@mode(compiled)` is not reported; the program silently does nothing.
- In `web.compiled`, the runner calls `main()`. `loup run file.lpweb` runs a
  file on the host interpreter, and errors print as
  `file:line:col: error[CODE]: message`.
- `loup-bootstrap init` creates a project; set `[build] entry` to your
  `.lpweb` file. `loup-bootstrap check` type-checks it.
- `loup-bootstrap build --target browser` writes a bare
  `build/<name>.wasm`, which `loup-launcher --run-bare` runs. Wasm output is
  experimental.
- The browser runtime, `loup serve`, and the reactive UI are planned.
