@lingxia/skill 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -0
- package/bin/install.mjs +247 -0
- package/package.json +49 -0
- package/scripts/sync.mjs +69 -0
- package/skill/SKILL.md +334 -0
- package/skill/app/apple-sdk.md +312 -0
- package/skill/app/applinks.md +289 -0
- package/skill/app/project.md +760 -0
- package/skill/cli/lxdev.md +195 -0
- package/skill/cli/reference.md +481 -0
- package/skill/examples/hello-host-js/README.md +25 -0
- package/skill/examples/hello-host-js/home/lxapp.json +12 -0
- package/skill/examples/hello-host-js/home/pages/home/index.json +4 -0
- package/skill/examples/hello-host-js/home/pages/home/index.ts +14 -0
- package/skill/examples/hello-host-js/home/pages/home/index.tsx +15 -0
- package/skill/examples/hello-host-js/lingxia.yaml +39 -0
- package/skill/examples/hello-host-rust/Cargo.toml +15 -0
- package/skill/examples/hello-host-rust/README.md +44 -0
- package/skill/examples/hello-host-rust/home/lxapp.json +13 -0
- package/skill/examples/hello-host-rust/home/pages/home/index.html +46 -0
- package/skill/examples/hello-host-rust/home/pages/home/index.json +4 -0
- package/skill/examples/hello-host-rust/lingxia.yaml +32 -0
- package/skill/examples/hello-host-rust/src/lib.rs +58 -0
- package/skill/examples/hello-lxapp/README.md +29 -0
- package/skill/examples/hello-lxapp/lxapp.config.ts +8 -0
- package/skill/examples/hello-lxapp/lxapp.json +14 -0
- package/skill/examples/hello-lxapp/package.json +14 -0
- package/skill/examples/hello-lxapp/pages/home/index.json +4 -0
- package/skill/examples/hello-lxapp/pages/home/index.ts +35 -0
- package/skill/examples/hello-lxapp/pages/home/index.tsx +34 -0
- package/skill/lxapp/bridge.md +654 -0
- package/skill/lxapp/components.md +375 -0
- package/skill/lxapp/guide.md +675 -0
- package/skill/lxapp/lx-api.md +481 -0
- package/skill/native/development.md +414 -0
- package/skill/reference/file-lifecycle.md +325 -0
- package/skill/skill-manifest.json +6 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# hello-host-js — Shape B: native host app with **JS Logic** lxapp
|
|
2
|
+
|
|
3
|
+
A macOS host shell that boots, opens one window, and renders an embedded "home" lxapp inside it. The home lxapp's Logic is **JavaScript** (`Page({})` in `index.ts`); the host compiles in the JS AppService runtime (`features.appService: true`). No Rust route — the View can still call Rust if you add one, but this example keeps the slate clean to focus on the host ↔ JS-lxapp wiring.
|
|
4
|
+
|
|
5
|
+
For the **Rust Logic** variant (no JS runtime, HTML-only view, `#[lingxia::native]` doing all the work), see [`../hello-host-rust/`](../hello-host-rust/README.md). The two examples differ only on the JS vs Rust Logic split; the host wiring is the same.
|
|
6
|
+
|
|
7
|
+
This is what `lingxia new my-app -t native-app -p macos --package-id com.example.my-app -y` produces in a minimal form. Real scaffolding adds platform-specific subdirectories under `macos/`, generated icons, and CI scripts; those are reproducible from this `lingxia.yaml` and not shown here.
|
|
8
|
+
|
|
9
|
+
Files:
|
|
10
|
+
|
|
11
|
+
- `lingxia.yaml` — host project source of truth: app metadata, target platform(s), feature flags, bundled lxapp sources, App UI (surfaces + activators).
|
|
12
|
+
- `home/` — the embedded "home" lxapp this host opens by default (same JS-Logic shape as [`../hello-lxapp/`](../hello-lxapp/README.md) — see that example for page Logic + View detail).
|
|
13
|
+
|
|
14
|
+
What to study:
|
|
15
|
+
|
|
16
|
+
- `app.homeAppId` matches `resources.bundles[].appId` matches the embedded `lxapp.json.appId`. **All three must agree** or the build fails / the wrong app launches.
|
|
17
|
+
- `ui.surfaces[].content.appId` points at the same `homeAppId` — that's what fills the macOS window.
|
|
18
|
+
- `features.appService: true` enables the JS Logic runtime. **Required** because the embedded lxapp has a `.ts` Logic file. (For the opposite case, see `../hello-host-rust/`.)
|
|
19
|
+
|
|
20
|
+
Cross-references:
|
|
21
|
+
|
|
22
|
+
- `lingxia.yaml` full reference (every field, every section): [`../../app/project.md`](../../app/project.md)
|
|
23
|
+
- macOS App UI (surfaces, activators, attach panels, menu-bar): [`../../app/project.md#macos-app-ui`](../../app/project.md#macos-app-ui)
|
|
24
|
+
- Apple SDK startup APIs (`Lingxia.quickStart()`): [`../../app/apple-sdk.md`](../../app/apple-sdk.md)
|
|
25
|
+
- Sibling Shape C example (Rust Logic): [`../hello-host-rust/`](../hello-host-rust/README.md)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Logic for the embedded home lxapp. Identical shape to ../hello-lxapp.
|
|
2
|
+
// The host shell opens this page via ui.launch.initialSurface -> surface
|
|
3
|
+
// -> content.appId -> lxapp.json.pages[0].
|
|
4
|
+
|
|
5
|
+
interface HomeData {
|
|
6
|
+
count: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
Page<HomeData>({
|
|
10
|
+
data: { count: 0 },
|
|
11
|
+
increment() {
|
|
12
|
+
this.setData({ count: this.data.count + 1 });
|
|
13
|
+
},
|
|
14
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useLxPage } from "@lingxia/react";
|
|
2
|
+
|
|
3
|
+
type PageData = { count: number };
|
|
4
|
+
type PageActions = { increment: () => void };
|
|
5
|
+
|
|
6
|
+
export default function HomePage() {
|
|
7
|
+
const { data, actions } = useLxPage<PageData, PageActions>();
|
|
8
|
+
return (
|
|
9
|
+
<main style={{ padding: 24 }}>
|
|
10
|
+
<h1>Hello from the host app</h1>
|
|
11
|
+
<p>Count: {data.count}</p>
|
|
12
|
+
<button onClick={() => actions.increment()}>+1</button>
|
|
13
|
+
</main>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Host app project config. The source of truth for the macOS build.
|
|
2
|
+
# `lingxia build` regenerates app.json + ui.json from this file; do not
|
|
3
|
+
# edit the generated files directly.
|
|
4
|
+
|
|
5
|
+
app:
|
|
6
|
+
projectName: hello-host
|
|
7
|
+
productName: Hello Host
|
|
8
|
+
productVersion: 0.1.0
|
|
9
|
+
platforms: [macos]
|
|
10
|
+
homeAppId: hello-home # must match resources.bundles[].appId below
|
|
11
|
+
|
|
12
|
+
macos:
|
|
13
|
+
bundleId: com.example.hello-host
|
|
14
|
+
deploymentTarget: "12.0"
|
|
15
|
+
targetName: HelloHost
|
|
16
|
+
executableName: HelloHost
|
|
17
|
+
|
|
18
|
+
features:
|
|
19
|
+
appService: true # the embedded lxapp uses JS Logic, so AppService must be on
|
|
20
|
+
shell: false
|
|
21
|
+
devtools: false
|
|
22
|
+
|
|
23
|
+
resources:
|
|
24
|
+
bundles:
|
|
25
|
+
- type: lxapp
|
|
26
|
+
appId: hello-home # same string as app.homeAppId
|
|
27
|
+
path: home # local directory containing lxapp.json + pages/
|
|
28
|
+
|
|
29
|
+
ui:
|
|
30
|
+
launch:
|
|
31
|
+
initialSurface: main
|
|
32
|
+
surfaces:
|
|
33
|
+
- id: main
|
|
34
|
+
presentation:
|
|
35
|
+
kind: window
|
|
36
|
+
content:
|
|
37
|
+
kind: lxapp
|
|
38
|
+
appId: hello-home # same id again — what to render inside the surface
|
|
39
|
+
activators: []
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Minimal host crate. `lingxia new` emits a workspace; this snippet shows
|
|
2
|
+
# only the slot that contains #[lingxia::native] routes and HostAddon.
|
|
3
|
+
|
|
4
|
+
[package]
|
|
5
|
+
name = "hello-native"
|
|
6
|
+
version = "0.1.0"
|
|
7
|
+
edition = "2021"
|
|
8
|
+
|
|
9
|
+
[lib]
|
|
10
|
+
crate-type = ["staticlib", "cdylib", "rlib"]
|
|
11
|
+
|
|
12
|
+
[dependencies]
|
|
13
|
+
lingxia = "0.7"
|
|
14
|
+
serde = { version = "1", features = ["derive"] }
|
|
15
|
+
tokio = { version = "1", features = ["rt", "macros", "time"] }
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# hello-host-rust — Shape C: host app with **Rust Logic, no JS runtime**
|
|
2
|
+
|
|
3
|
+
A native host app whose embedded home lxapp is **HTML-only and has no JS Logic layer**. All state and behavior live in the Rust crate, exposed to the View through `#[lingxia::native]` routes. The JS AppService runtime is not even compiled into the host (`features.appService: false`).
|
|
4
|
+
|
|
5
|
+
Pick this shape for: menu-bar utilities, system-tray tools, Rust-heavy desktop apps where the UI is mostly a thin view over native logic.
|
|
6
|
+
|
|
7
|
+
## Contrast with [`../hello-host-js/`](../hello-host-js/README.md)
|
|
8
|
+
|
|
9
|
+
Same host shell, opposite Logic side:
|
|
10
|
+
|
|
11
|
+
| | `hello-host-js` (Shape B) | `hello-host-rust` (Shape C) |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| `features.appService` | `true` | **`false`** |
|
|
14
|
+
| `lxapp.json.logic` | (omitted, defaults true) | **`false`** |
|
|
15
|
+
| Logic layer | `Page({ data, …actions })` in `index.ts` | **Rust** `#[lingxia::native]` routes |
|
|
16
|
+
| View | React `index.tsx` + `useLxPage` | **plain `index.html` + `<script>`** |
|
|
17
|
+
| Bridge surface | `actions.foo()` from `useLxPage()` | `window.native.foo(...)` (CLI-generated browser global) |
|
|
18
|
+
| JS AppService runtime in host binary | ✅ shipped | **❌ not compiled in** (smaller binary) |
|
|
19
|
+
| Cargo dep on `lingxia` | optional | **required** |
|
|
20
|
+
|
|
21
|
+
The host project shape (`lingxia.yaml` `ui`, `resources.bundles`, FFI export) is identical. Only the lxapp's authoring model and the `features.appService` switch differ.
|
|
22
|
+
|
|
23
|
+
## Files
|
|
24
|
+
|
|
25
|
+
- `lingxia.yaml` — `features.appService: false`; everything else like hello-host-js.
|
|
26
|
+
- `Cargo.toml` — host Rust crate declaration.
|
|
27
|
+
- `src/lib.rs` — `#[lingxia::native("greet.hello")]` route + `HostAddon` + platform FFI export. (Same shape as a Shape B route — the route signature doesn't change between B and C.)
|
|
28
|
+
- `home/lxapp.json` — has `"logic": false` flag; the runtime won't try to spin up a JS Logic layer.
|
|
29
|
+
- `home/pages/home/index.html` — the entire UI. `<script src="lingxia://lxapp/.lingxia/native.js">` auto-loads the CLI-generated client; the click handler calls `window.native.greet.hello(...)`.
|
|
30
|
+
- `home/pages/home/index.json` — page-level config (navigation bar). The same shape works for both B and C.
|
|
31
|
+
|
|
32
|
+
## What to study
|
|
33
|
+
|
|
34
|
+
- **`features.appService: false`** disables the JS runtime. A logic-enabled lxapp (no `"logic": false`) paired with this is **rejected at startup** — flip both together.
|
|
35
|
+
- **`"logic": false`** in `lxapp.json` is the lxapp-side declaration that there is no `Page({})` Logic. The CLI build won't look for `pages/*/index.ts` files; the page is the `index.html`.
|
|
36
|
+
- **No `@lingxia/react` / `@lingxia/vue` / `@lingxia/html`** dependency. Those packages all assume the AppService bridge (`Page({})` shape). Use plain DOM APIs.
|
|
37
|
+
- **`window.native.*`** is the CLI-generated **browser-global** client (different output format from the `@lingxia/native` module client used by React/Vue Shape B). The HTML view just `<script>`-loads it; no bundler involved.
|
|
38
|
+
- **The Rust route is identical** to a Shape B route — `#[lingxia::native]`, `HostAddon::install_host_apis`, the macro-generated `<fn>_host()` companion. Only which side owns Logic differs.
|
|
39
|
+
|
|
40
|
+
## Cross-references in this skill
|
|
41
|
+
|
|
42
|
+
- Full native Rust development (streams, channels, facades, `lingxia::js` extensions, Android/Harmony FFI): [`../../native/development.md`](../../native/development.md)
|
|
43
|
+
- `lingxia.yaml` full reference including the `features` section: [`../../app/project.md`](../../app/project.md)
|
|
44
|
+
- Sibling Shape B example: [`../hello-host-js/`](../hello-host-js/README.md)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"appId": "hello-home",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"appName": "Hello Host (Rust Logic)",
|
|
5
|
+
"logic": false,
|
|
6
|
+
"pages": [
|
|
7
|
+
{ "name": "home", "path": "pages/home/index" }
|
|
8
|
+
],
|
|
9
|
+
"security": {
|
|
10
|
+
"network": { "trustedDomains": [] },
|
|
11
|
+
"privileges": []
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<title>Hello Host (Rust Logic)</title>
|
|
6
|
+
<!--
|
|
7
|
+
Shape C: no @lingxia/html, no Page({}), no useLxPage.
|
|
8
|
+
The runtime auto-injects the CLI-generated native client at
|
|
9
|
+
lingxia://lxapp/.lingxia/native.js, exposing `window.native.*`.
|
|
10
|
+
All state and "Logic" live in the Rust crate at ../../../src/lib.rs.
|
|
11
|
+
-->
|
|
12
|
+
<script src="lingxia://lxapp/.lingxia/native.js"></script>
|
|
13
|
+
<style>
|
|
14
|
+
body { font-family: system-ui; padding: 24px; }
|
|
15
|
+
button { font-size: 16px; padding: 8px 16px; }
|
|
16
|
+
#out { margin-top: 16px; font-size: 18px; }
|
|
17
|
+
</style>
|
|
18
|
+
</head>
|
|
19
|
+
<body>
|
|
20
|
+
<h1>Hello Host (Rust Logic)</h1>
|
|
21
|
+
<button id="say">Call greet.hello</button>
|
|
22
|
+
<p id="out"></p>
|
|
23
|
+
|
|
24
|
+
<script>
|
|
25
|
+
const btn = document.getElementById("say");
|
|
26
|
+
const out = document.getElementById("out");
|
|
27
|
+
|
|
28
|
+
btn.addEventListener("click", async () => {
|
|
29
|
+
btn.disabled = true;
|
|
30
|
+
btn.textContent = "Calling Rust…";
|
|
31
|
+
try {
|
|
32
|
+
// window.native is the CLI-generated browser-global client.
|
|
33
|
+
// Each #[lingxia::native("ns.method")] route becomes
|
|
34
|
+
// window.native.ns.method(...).
|
|
35
|
+
const res = await window.native.greet.hello({ name: "World" });
|
|
36
|
+
out.textContent = res.greeting;
|
|
37
|
+
} catch (err) {
|
|
38
|
+
out.textContent = `Error: ${err && err.message ? err.message : err}`;
|
|
39
|
+
} finally {
|
|
40
|
+
btn.disabled = false;
|
|
41
|
+
btn.textContent = "Call greet.hello";
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
</script>
|
|
45
|
+
</body>
|
|
46
|
+
</html>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
app:
|
|
2
|
+
projectName: hello-host-rust
|
|
3
|
+
productName: Hello Host (Rust Logic)
|
|
4
|
+
productVersion: 0.1.0
|
|
5
|
+
platforms: [macos]
|
|
6
|
+
homeAppId: hello-home
|
|
7
|
+
|
|
8
|
+
macos:
|
|
9
|
+
bundleId: com.example.hello-host-rust
|
|
10
|
+
deploymentTarget: "12.0"
|
|
11
|
+
targetName: HelloHostRust
|
|
12
|
+
executableName: HelloHostRust
|
|
13
|
+
|
|
14
|
+
features:
|
|
15
|
+
appService: false # Shape C: no JS AppService runtime. All Logic lives in Rust.
|
|
16
|
+
shell: false
|
|
17
|
+
devtools: false
|
|
18
|
+
|
|
19
|
+
resources:
|
|
20
|
+
bundles:
|
|
21
|
+
- type: lxapp
|
|
22
|
+
appId: hello-home
|
|
23
|
+
path: home
|
|
24
|
+
|
|
25
|
+
ui:
|
|
26
|
+
launch:
|
|
27
|
+
initialSurface: main
|
|
28
|
+
surfaces:
|
|
29
|
+
- id: main
|
|
30
|
+
presentation: { kind: window }
|
|
31
|
+
content: { kind: lxapp, appId: hello-home }
|
|
32
|
+
activators: []
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Host Rust library. Exposes one route to the View via #[lingxia::native].
|
|
2
|
+
//
|
|
3
|
+
// The macro generates a sibling fn `<name>_host()` that returns the
|
|
4
|
+
// host-entry registration value — you do NOT write that fn yourself, and
|
|
5
|
+
// you cannot rename it.
|
|
6
|
+
|
|
7
|
+
use std::sync::Arc;
|
|
8
|
+
|
|
9
|
+
#[derive(serde::Deserialize)]
|
|
10
|
+
struct HelloInput {
|
|
11
|
+
name: String,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
#[derive(serde::Serialize)]
|
|
15
|
+
struct HelloOutput {
|
|
16
|
+
greeting: String,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Parameter ordering — enforced by the macro:
|
|
20
|
+
// 1. `Arc<lingxia::LxApp>` (optional, must be FIRST when present)
|
|
21
|
+
// 2. JSON payload (any serde::Deserialize type) (optional)
|
|
22
|
+
// 3. `lingxia::host::HostCancel` (optional, must be LAST when present)
|
|
23
|
+
//
|
|
24
|
+
// Return type must be `lingxia::Result<T>` where T: serde::Serialize.
|
|
25
|
+
#[lingxia::native("greet.hello")]
|
|
26
|
+
async fn greet_hello(
|
|
27
|
+
_app: Arc<lingxia::LxApp>,
|
|
28
|
+
input: HelloInput,
|
|
29
|
+
) -> lingxia::Result<HelloOutput> {
|
|
30
|
+
Ok(HelloOutput {
|
|
31
|
+
greeting: format!("Hello, {}!", input.name),
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
struct AppHostAddon;
|
|
36
|
+
|
|
37
|
+
impl lingxia::HostAddon for AppHostAddon {
|
|
38
|
+
fn install_host_apis(&self) {
|
|
39
|
+
// greet_hello_host() is generated by the #[lingxia::native] macro
|
|
40
|
+
// applied to `greet_hello` above. Register one per route.
|
|
41
|
+
lingxia::host::register_host_entry(greet_hello_host());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fn start_services(&self) {
|
|
45
|
+
// Background services go here (devtool bridge, push, ipc, …).
|
|
46
|
+
// None in this example.
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Platform FFI export. Apple platforms call this symbol from the SDK at
|
|
51
|
+
// startup. Android uses a `Java_<package>_<class>_nativeRegisterHostAddon`
|
|
52
|
+
// JNI entrypoint; Harmony uses a `#[napi]`-annotated function. See the
|
|
53
|
+
// skill's native/development.md for the full set.
|
|
54
|
+
#[cfg(any(target_os = "ios", target_os = "macos"))]
|
|
55
|
+
#[unsafe(no_mangle)]
|
|
56
|
+
pub extern "C" fn lingxia_register_host_addon() {
|
|
57
|
+
lingxia::register_host_addon(Box::new(AppHostAddon));
|
|
58
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# hello-lxapp — Shape A: a standalone lxapp
|
|
2
|
+
|
|
3
|
+
A page-based mini-app. Runs in any LingXia host (e.g. macOS Runner via `lingxia dev`). No native shell, no Rust.
|
|
4
|
+
|
|
5
|
+
This is the smallest meaningful starter. **Read it; don't copy-paste it whole** — scaffold a real project with `lingxia new my-app -t lxapp -y` and refer back here for shape.
|
|
6
|
+
|
|
7
|
+
Files:
|
|
8
|
+
|
|
9
|
+
- `lxapp.json` — appId, version, pages list, security policy
|
|
10
|
+
- `lxapp.config.ts` — build config (view framework, static dirs, …)
|
|
11
|
+
- `package.json` — runtime + dev deps you actually need
|
|
12
|
+
- `pages/home/`
|
|
13
|
+
- `index.ts` — **Logic** (`Page({…})`) — state and actions, runs in the native JS runtime
|
|
14
|
+
- `index.tsx` — **View** (React + `useLxPage`) — renders UI, runs in the WebView
|
|
15
|
+
- `index.json` — page-level config (navigation bar, etc.)
|
|
16
|
+
|
|
17
|
+
What to study:
|
|
18
|
+
|
|
19
|
+
- How **Logic** initializes `data: {…}` and exposes public methods to View.
|
|
20
|
+
- How `_privateHelper` is hidden from View by the `_` prefix convention.
|
|
21
|
+
- How **View** types `PageData` / `PageActions` with **required** fields (the runtime guarantees first-paint data and wired-up actions).
|
|
22
|
+
- How `lxapp.json.security.network.trustedDomains` is set to `[]` to deny all external `fetch()` calls — flip to actual hostnames before running anything that talks to a server.
|
|
23
|
+
|
|
24
|
+
Cross-references in this skill:
|
|
25
|
+
|
|
26
|
+
- Full page authoring: [`../../lxapp/guide.md`](../../lxapp/guide.md)
|
|
27
|
+
- Bridge mechanics (`setData`, stream, channel): [`../../lxapp/bridge.md`](../../lxapp/bridge.md)
|
|
28
|
+
- Native components (`LxInput`, `LxVideo`, …): [`../../lxapp/components.md`](../../lxapp/components.md)
|
|
29
|
+
- Logic-side `lx.*` API: [`../../lxapp/lx-api.md`](../../lxapp/lx-api.md)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// LxApp build config. Loaded by `lingxia build` / `lingxia dev`.
|
|
2
|
+
// View framework is auto-detected from page file extensions (.tsx → react,
|
|
3
|
+
// .vue → vue, .html → html). `staticDirs` declares root folders copied
|
|
4
|
+
// verbatim into dist/.
|
|
5
|
+
export default {
|
|
6
|
+
view: { framework: "react" },
|
|
7
|
+
staticDirs: ["public"],
|
|
8
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hello-lxapp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"@lingxia/react": "^0.7.0",
|
|
7
|
+
"react": "^18.0.0",
|
|
8
|
+
"react-dom": "^18.0.0"
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@lingxia/types": "^0.7.0",
|
|
12
|
+
"typescript": "^5.3.0"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Logic layer. Runs in the native JS runtime (not the WebView).
|
|
2
|
+
// `Page` is a global from @lingxia/types — no import needed.
|
|
3
|
+
// `_`-prefixed methods are private to Logic and NOT exposed as View actions.
|
|
4
|
+
|
|
5
|
+
interface HomeData {
|
|
6
|
+
count: number;
|
|
7
|
+
message: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
Page<HomeData>({
|
|
11
|
+
data: {
|
|
12
|
+
count: 0,
|
|
13
|
+
message: "Tap to increment",
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
onLoad(options) {
|
|
17
|
+
// `options` is the URL query that opened this page.
|
|
18
|
+
// Useful for routing — ignored in the simplest case.
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
increment() {
|
|
22
|
+
// `this.data` is read-only. Always go through `setData` so the View
|
|
23
|
+
// sees the change.
|
|
24
|
+
this.setData({ count: this.data.count + 1 });
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
reset() {
|
|
28
|
+
this.setData({ count: 0 });
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
_doubled() {
|
|
32
|
+
// Private helper. Not visible to View.
|
|
33
|
+
return this.data.count * 2;
|
|
34
|
+
},
|
|
35
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// View layer. Runs in the WebView.
|
|
2
|
+
// `useLxPage` returns `{ data, actions }` — data is reactive, actions
|
|
3
|
+
// are bridge functions wired to the public methods on Page({}).
|
|
4
|
+
//
|
|
5
|
+
// Type fields as REQUIRED unless your Logic actually populates them
|
|
6
|
+
// lazily — the runtime guarantees both `data` and `actions` are wired
|
|
7
|
+
// by first paint. All-optional types create `?.()` and `??` noise.
|
|
8
|
+
import { useLxPage } from "@lingxia/react";
|
|
9
|
+
|
|
10
|
+
type PageData = {
|
|
11
|
+
count: number;
|
|
12
|
+
message: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type PageActions = {
|
|
16
|
+
increment: () => void;
|
|
17
|
+
reset: () => void;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default function HomePage() {
|
|
21
|
+
const { data, actions } = useLxPage<PageData, PageActions>();
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<main style={{ padding: 24, fontFamily: "system-ui" }}>
|
|
25
|
+
<h1>Hello LxApp</h1>
|
|
26
|
+
<p>{data.message}</p>
|
|
27
|
+
<p style={{ fontSize: 48 }}>{data.count}</p>
|
|
28
|
+
<button onClick={() => actions.increment()}>+1</button>
|
|
29
|
+
<button onClick={() => actions.reset()} style={{ marginLeft: 8 }}>
|
|
30
|
+
Reset
|
|
31
|
+
</button>
|
|
32
|
+
</main>
|
|
33
|
+
);
|
|
34
|
+
}
|