92 lines
3.6 KiB
Markdown

# Sage desktop client sample
This directory is an independent Compose Desktop sample app for Sage on top of the
root `bricks-mp` shared module. It is intentionally **not** included from the
root `settings.gradle.kts`, so normal library builds are unchanged.
## Build
From any directory:
```bash
/path/to/bricks-mp/test/sageclient/build.sh
```
The script:
1. resolves its own directory,
2. uses JDK 17 or JDK 21; on macOS it auto-selects an installed 21/17 via `/usr/libexec/java_home` when the active Java is unsupported,
3. uses the repository Gradle wrapper when present, otherwise system `gradle`,
4. runs `gradle build` for this standalone sample.
Kotlin 2.1.0 / this Gradle setup does not work with newer Java versions such as `26.0.1`. If your default `java -version` is 26, install JDK 21 or 17 and either let `build.sh` auto-detect it or export `JAVA_HOME` explicitly:
```bash
brew install --cask temurin@21
export JAVA_HOME=$(/usr/libexec/java_home -v 21)
./test/sageclient/build.sh
```
Extra Gradle arguments can be appended, for example:
```bash
./test/sageclient/build.sh --info
```
## Run
Run without arguments to open the sample shell:
```bash
cd /path/to/bricks-mp/test/sageclient
../../gradlew run \
-Dsage.baseUrl=http://localhost:8080 \
-Dsage.centerUi=/center.ui
```
Run with a startup URL to enter that UI directly without showing the sample
base URL / login input shell:
```bash
cd /path/to/bricks-mp/test/sageclient
../../gradlew run --args="https://ai.atvoe.com/center.ui"
```
The packaged macOS app also accepts the URL as its first argument:
```bash
open build/compose/binaries/main/app/sageclient.app --args https://ai.atvoe.com/center.ui
```
If the argument is an absolute `http://` or `https://` URL, `sageclient` uses the URL origin as `baseUrl` and dispatches that same absolute URL through the Bricks `urlwidget` loading path. If the argument is a relative path such as `/center.ui`, it uses `sage.baseUrl` and loads that path. The actual HTTP request is still issued through `BricksHttp`, so startup URLs ending in `.ui` / `.dspy` are requested with `_webbricks_=1`, `_width`, `_height`, `_is_mobile` and `_lang`; they must not be fetched as raw HTML/template pages.
Optional properties:
- `sage.baseUrl` defaults to `http://localhost:8080`
- `sage.centerUi` defaults to `/center.ui`
- `sage.loginAction` defaults to `/rbac/user/login`
- `sage.loginUi` defaults to `/rbac/user/login.ui`
- `sage.username` / `sage.password` prefill the login form
- `sage.lang` overrides the default JVM locale tag
## What the sample demonstrates
- Uses the generic `BricksHttp` client from the shared module for Sage login and
`center.ui` loading. Sage-specific bootstrapping lives here, not in the shared
library package.
- Loads `center.ui` via `ActionDispatcher.dispatch(BricksBind(actiontype =
"urlwidget", ...))`, so HTTP handling stays in the shared ActionDispatcher /
BricksHttp flow.
- `BricksHttp` automatically appends the correct WebBricks query parameters for
`.ui` / `.dspy` backend requests:
`_webbricks_`, `_width`, `_height`, `_is_mobile`, `_lang`.
- `BricksHttp` surfaces HTTP 403, 401 and 3xx (including 301) as
`BricksHttpException`. `ActionDispatcher` handles them generically:
- 403 loads the configured login UI (`sage.loginUi`) in a dialog,
- 401 shows the server response as an unauthorized message,
- 3xx follows the `Location` header as a UI navigation target.
The sample is wired as a Gradle composite build through `includeBuild("../..")`
and depends on the root project module using `implementation("com.bricks.mp:shared")`
with dependency substitution to `:shared`.