@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,481 @@
|
|
|
1
|
+
# Logic-side `lx.*` API
|
|
2
|
+
|
|
3
|
+
Every lxapp Logic file (`pages/*/index.ts`) runs against a global `lx` object that exposes platform capabilities — navigation, file I/O, media, networking, device info, UI chrome, and more. The shape of `lx` (and the `Page({})` / `App({})` globals) is published as TypeScript declarations in **`@lingxia/types`**.
|
|
4
|
+
|
|
5
|
+
This page is the routing index for `lx.*`. It tells you which namespace owns which capability and how to wire up typing.
|
|
6
|
+
|
|
7
|
+
For page mechanics (`data`, `setData`, lifecycle), see [`./guide.md`](./guide.md).
|
|
8
|
+
For bridge details (stream, channel), see [`./bridge.md`](./bridge.md).
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Install typing
|
|
13
|
+
|
|
14
|
+
`@lingxia/types` declares everything globally — no `import` needed in Logic files.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install --save-dev @lingxia/types@<lingxia-version>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Match the version to your `lingxia` CLI. The skill's `package.json` version, the CLI version, and `@lingxia/types` are released in lockstep.
|
|
21
|
+
|
|
22
|
+
Then in `tsconfig.json`:
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"compilerOptions": {
|
|
27
|
+
"types": ["@lingxia/types"]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
That's it. `lx`, `Page`, `App`, `getApp`, and `getCurrentPages` are now globally typed.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
// pages/home/index.ts — no imports needed for the lx surface
|
|
36
|
+
Page({
|
|
37
|
+
data: { name: '' },
|
|
38
|
+
|
|
39
|
+
async onLoad() {
|
|
40
|
+
const info = lx.getDeviceInfo(); // typed
|
|
41
|
+
lx.setNavigationBarTitle({ title: `Hello, ${info.model}` });
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
async pickFile() {
|
|
45
|
+
const res = await lx.chooseFile({ count: 1 });
|
|
46
|
+
this.setData({ name: res.files[0]?.name ?? '' });
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Globals
|
|
54
|
+
|
|
55
|
+
| Global | Purpose |
|
|
56
|
+
|---|---|
|
|
57
|
+
| `lx` | The full platform API surface (see namespaces below). |
|
|
58
|
+
| `Page(config)` | Define a page. `config.data` initializes state; public methods become bridge-callable actions. |
|
|
59
|
+
| `App(config)` | Define the app-wide lifecycle (`onLaunch`, `onShow`, `onHide`, …). |
|
|
60
|
+
| `getApp<T>()` | Return the current `AppInstance` or `null`. |
|
|
61
|
+
| `getCurrentPages<T>()` | Stack of currently mounted pages, top of stack last. |
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Standard Web APIs (built-in globals)
|
|
66
|
+
|
|
67
|
+
The Logic JS runtime is **not** a stripped-down sandbox. It's the [Rong](https://github.com/) runtime with the standard Web API set wired in, so you write Logic code the same way you'd write any modern JS — `fetch`, `setTimeout`, `URL`, `console`, all available globally with no import.
|
|
68
|
+
|
|
69
|
+
Available everywhere (every lxapp Logic file):
|
|
70
|
+
|
|
71
|
+
| Group | Globals provided |
|
|
72
|
+
|---|---|
|
|
73
|
+
| **Timers** | `setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`, `queueMicrotask` |
|
|
74
|
+
| **HTTP** | `fetch`, `Request`, `Response`, `Headers`, `FormData` |
|
|
75
|
+
| **Encoding** | `TextEncoder`, `TextDecoder`, `btoa`, `atob` |
|
|
76
|
+
| **URL** | `URL`, `URLSearchParams` |
|
|
77
|
+
| **Streams** | `ReadableStream`, `WritableStream`, `TransformStream`, `ByteLengthQueuingStrategy`, `CountQueuingStrategy` |
|
|
78
|
+
| **Events / abort** | `Event`, `EventTarget`, `CustomEvent`, `AbortController`, `AbortSignal` |
|
|
79
|
+
| **Exception** | `DOMException` |
|
|
80
|
+
| **Buffer** | `Buffer` (Node-style — handy for binary work alongside `ArrayBuffer`) |
|
|
81
|
+
| **Console** | `console.log`, `console.info`, `console.warn`, `console.error`, `console.debug`, `console.trace` |
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
// Standard fetch, just works.
|
|
85
|
+
const res = await fetch('https://api.example.com/items', {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
headers: { 'content-type': 'application/json' },
|
|
88
|
+
body: JSON.stringify({ id: 42 }),
|
|
89
|
+
signal: AbortSignal.timeout(5000),
|
|
90
|
+
});
|
|
91
|
+
const data = await res.json();
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Gating.** `fetch` (and `WebSocket` if you reach for it) is constrained by the lxapp's `security.network.trustedDomains` in `lxapp.json`. A request to a host that isn't on that list silently fails — see [LxApp guide → Security Policy](./guide.md#security-policy).
|
|
95
|
+
|
|
96
|
+
### AppService-only extras
|
|
97
|
+
|
|
98
|
+
When the host has `features.appService: true`, the wider **AppService scope** (the JS service that hosts all per-page Logic contexts) adds:
|
|
99
|
+
|
|
100
|
+
- **`cron`** — scheduled-task module for app-lifetime jobs. Useful for periodic checks (heartbeat, badge refresh, polling) that should run as long as the lxapp is loaded, not tied to a single page lifecycle.
|
|
101
|
+
- **App-wide `storage`** — durable key/value at the lxapp scope.
|
|
102
|
+
|
|
103
|
+
The Rong-supplied cron surface isn't yet declared in `@lingxia/types`; check the runtime's current globals (e.g. via `console.log(globalThis)` from `App({}).onLaunch`) for the exact API, or look at `crates/lingxia-lxapp/Cargo.toml`'s `rong_modules` feature list for what's enabled. App-scope key/value is available via the page-level `lx.getStorage()` (see [Storage](#storage-keyvalue)) — values written there persist across pages and app launches.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## `lx` surface — by capability
|
|
108
|
+
|
|
109
|
+
The `lx` object is flat (no nested namespaces in code), but the surface logically groups into the capabilities below. The "Sub-module" column shows where the types come from in `@lingxia/types/<sub>` if you want to import option/result types directly.
|
|
110
|
+
|
|
111
|
+
### Navigation (in-app and cross-lxapp)
|
|
112
|
+
|
|
113
|
+
Sub-module: `@lingxia/types/navigator`, `@lingxia/types/app`
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
lx.navigateTo(options) // push a page
|
|
117
|
+
lx.navigateBack(options) // pop
|
|
118
|
+
lx.redirectTo(options) // replace current page
|
|
119
|
+
lx.switchTab(options) // switch to tab page
|
|
120
|
+
lx.reLaunch(options) // restart at a new page
|
|
121
|
+
|
|
122
|
+
lx.navigateToLxApp(options) // jump to another lxapp
|
|
123
|
+
lx.navigateBackLxApp() // return to caller lxapp
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
For declarative navigation in markup, prefer the `LxNavigator` component — see [`./components.md` → LxNavigator](./components.md#lxnavigator).
|
|
127
|
+
|
|
128
|
+
### Page chrome / UI
|
|
129
|
+
|
|
130
|
+
Sub-module: `@lingxia/types/ui`
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
lx.setNavigationBarTitle({ title })
|
|
134
|
+
lx.setNavigationBarColor(options)
|
|
135
|
+
lx.hideHomeButton()
|
|
136
|
+
|
|
137
|
+
lx.showToast(options) / lx.hideToast()
|
|
138
|
+
lx.showModal(options) -> Promise<ModalResult>
|
|
139
|
+
lx.showActionSheet(options) -> Promise<ActionSheetResult>
|
|
140
|
+
|
|
141
|
+
lx.showTabBar() / hideTabBar()
|
|
142
|
+
lx.setTabBarStyle(options)
|
|
143
|
+
lx.setTabBarItem(options)
|
|
144
|
+
lx.setTabBarBadge(options) / removeTabBarBadge(options)
|
|
145
|
+
lx.showTabBarRedDot(options) / hideTabBarRedDot(options)
|
|
146
|
+
|
|
147
|
+
lx.startPullDownRefresh() / stopPullDownRefresh()
|
|
148
|
+
lx.getCapsuleRect() -> Promise<CapsuleRect>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
> The `setTabBar*` family mutates an already-declared tab bar — the tab bar itself is configured statically in `lxapp.json`. For the declarative shape, switching tabs (`lx.switchTab`), and the rule that tab bar is an lxapp-internal concept (unrelated to host App UI surfaces), see [LxApp guide → Tab bar navigation](./guide.md#tab-bar-navigation).
|
|
152
|
+
|
|
153
|
+
### Media (images, video, scanning, preview)
|
|
154
|
+
|
|
155
|
+
Sub-module: `@lingxia/types/media`
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
lx.chooseMedia(options?) -> Promise<ChosenMediaEntry[]>
|
|
159
|
+
lx.previewMedia(options) -> PreviewMediaHandle // returns handle synchronously
|
|
160
|
+
lx.saveImageToPhotosAlbum(options)
|
|
161
|
+
lx.saveVideoToPhotosAlbum(options)
|
|
162
|
+
|
|
163
|
+
lx.getImageInfo(options) -> Promise<ImageInfo>
|
|
164
|
+
lx.compressImage(options) -> Promise<CompressImageResult>
|
|
165
|
+
lx.compressVideo(options) -> Promise<CompressVideoResult>
|
|
166
|
+
lx.getVideoInfo(options) -> Promise<VideoInfo>
|
|
167
|
+
lx.extractVideoThumbnail(options) -> Promise<ExtractVideoThumbnailResult>
|
|
168
|
+
|
|
169
|
+
lx.scanCode(options?) -> Promise<ScanCodeResult>
|
|
170
|
+
|
|
171
|
+
// Imperative video player control (pair with a <LxVideo id=…>)
|
|
172
|
+
lx.createVideoContext(componentId) -> VideoContext
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`previewMedia` returns a handle, not a promise. Await `handle.completed` for the final session result; subscribe to `handle.presented` to know when the first pixel hits the screen.
|
|
176
|
+
|
|
177
|
+
### File and transfer
|
|
178
|
+
|
|
179
|
+
Sub-module: `@lingxia/types/file`, `@lingxia/types/transfer`
|
|
180
|
+
|
|
181
|
+
Top-level file operations (open in a native viewer, pick a file, transfer):
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
lx.openFile(options) // mode: 'auto' | 'review'
|
|
185
|
+
lx.chooseFile(options?) -> Promise<ChooseFileResult>
|
|
186
|
+
lx.chooseDirectory(options?) -> Promise<ChooseDirectoryResult>
|
|
187
|
+
|
|
188
|
+
lx.downloadFile(options) -> DownloadTask
|
|
189
|
+
lx.uploadFile(options) -> UploadTask
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**`DownloadTask` / `UploadTask`** are both `PromiseLike` **and** `AsyncIterable` — `await` them for the final result, or iterate for live progress events:
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
// Simple form — await for the final result
|
|
196
|
+
const result = await lx.downloadFile({ url: 'https://cdn.example.com/big.zip', filePath: 'lingxia://cache/big.zip' });
|
|
197
|
+
|
|
198
|
+
// Progress form — iterate
|
|
199
|
+
const task = lx.downloadFile({ url, filePath });
|
|
200
|
+
for await (const event of task) {
|
|
201
|
+
// event.progress: 0..100 ; event.totalBytesWritten / event.totalBytesExpected
|
|
202
|
+
setData({ percent: event.progress });
|
|
203
|
+
}
|
|
204
|
+
const final = await task.wait();
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Control methods (all `Promise<void>`):
|
|
208
|
+
|
|
209
|
+
| Method | DownloadTask | UploadTask | Notes |
|
|
210
|
+
|---|:---:|:---:|---|
|
|
211
|
+
| `pause()` | ✓ | — | Pauses bytes flowing; `resume()` continues. Not all backends support it. |
|
|
212
|
+
| `resume()` | ✓ | — | |
|
|
213
|
+
| `cancel()` | ✓ | ✓ | Aborts the underlying transfer; the task promise rejects. |
|
|
214
|
+
| `abort()` | ✓ | — | Alias for `cancel()` — matches browser / mini-program naming. |
|
|
215
|
+
| `wait()` | ✓ | ✓ | Awaits the final result. Equivalent to `await task`. Use when you stopped iterating partway. |
|
|
216
|
+
|
|
217
|
+
`return()` on the iterator (e.g. `break` inside `for await`) stops iteration **without** cancelling the underlying transfer — call `cancel()` explicitly if you want to abort.
|
|
218
|
+
|
|
219
|
+
For low-level read/write/stat/list/mkdir/copy/rename/remove, get a `FileManager`:
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
const fm = lx.getFileManager();
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
`FileManager` methods (every method returns a `Promise<…>` — async-only):
|
|
226
|
+
|
|
227
|
+
| Method | Signature | Notes |
|
|
228
|
+
|---|---|---|
|
|
229
|
+
| `exists` | `({ path }) → boolean` | |
|
|
230
|
+
| `stat` | `({ path }) → FileStats` | `{ isFile, isDirectory, isSymlink, size, lastModifiedTime?, lastAccessedTime?, createTime? }` |
|
|
231
|
+
| `readDir` | `({ path }) → AsyncIterableIterator<DirEntry>` | `for await (const entry of await fm.readDir(...))` — `DirEntry = { name, isFile, isDirectory, isSymlink }` |
|
|
232
|
+
| `mkdir` | `({ path, recursive? })` | `recursive: true` for `mkdir -p` behavior |
|
|
233
|
+
| `readFile` | `({ filePath, encoding: 'utf8' \| 'base64' }) → { data: string }`<br>or `({ filePath }) → { data: ArrayBuffer }` | Pass `encoding` for text, omit for binary. Three overloads share one impl. |
|
|
234
|
+
| `writeFile` | `({ filePath, data: string, encoding?: 'utf8' \| 'base64', overwrite? })`<br>or `({ filePath, data: ArrayBuffer \| ArrayBufferView, overwrite? })` | `overwrite` defaults to `false` — write fails if target exists. |
|
|
235
|
+
| `copyFile` | `({ srcPath, destPath, overwrite? })` | |
|
|
236
|
+
| `rename` | `({ oldPath, newPath, overwrite? })` | Use for moves too. |
|
|
237
|
+
| `remove` | `({ path, recursive? })` | `recursive: true` removes directories with content. |
|
|
238
|
+
|
|
239
|
+
`path` / `filePath` strings use the storage-class scheme described in [`../reference/file-lifecycle.md`](../reference/file-lifecycle.md) (e.g. `lingxia://temp/...`, `lingxia://cache/...`). That doc also covers when each storage class is auto-cleaned and how `downloadFile` paths interact with the lifecycle.
|
|
240
|
+
|
|
241
|
+
### Device / system
|
|
242
|
+
|
|
243
|
+
Sub-module: `@lingxia/types/device`, `@lingxia/types/system`
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
lx.getDeviceInfo() -> DeviceInfo
|
|
247
|
+
lx.getScreenInfo() -> ScreenInfo
|
|
248
|
+
lx.getSystemSetting() -> SystemSettingInfo
|
|
249
|
+
lx.vibrateShort() / vibrateLong()
|
|
250
|
+
lx.makePhoneCall(options)
|
|
251
|
+
lx.openURL(options) // hand off to OS browser/app
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Networking
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
lx.startWifi() / stopWifi()
|
|
258
|
+
lx.connectWifi(options) -> Promise<void>
|
|
259
|
+
lx.getWifiList() -> Promise<WifiInfo[]>
|
|
260
|
+
lx.getConnectedWifi() -> Promise<WifiInfo>
|
|
261
|
+
lx.onWifiConnected(cb) / offWifiConnected(cb?)
|
|
262
|
+
|
|
263
|
+
lx.getNetworkInfo() -> Promise<NetworkInfo>
|
|
264
|
+
lx.onNetworkChange(cb) / offNetworkChange(cb?)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Network requests from Logic must respect `security.network.trustedDomains` in `lxapp.json` — see [`./guide.md` → Security Policy](./guide.md#security-policy). The `lx.*` networking calls above are for WiFi / network-info — for actual **HTTP requests**, use the standard global `fetch` (see [Standard Web APIs](#standard-web-apis-built-in-globals)).
|
|
268
|
+
|
|
269
|
+
### Display / orientation
|
|
270
|
+
|
|
271
|
+
```ts
|
|
272
|
+
lx.setDeviceOrientation(orientation)
|
|
273
|
+
lx.onDeviceOrientationChange(cb) / offDeviceOrientationChange(cb?)
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Location
|
|
277
|
+
|
|
278
|
+
```ts
|
|
279
|
+
lx.getLocation(options?) -> Promise<LocationInfo>
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Keyboard / hardware input
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
lx.onKeyDown(cb) / offKeyDown(cb?)
|
|
286
|
+
lx.onKeyUp(cb) / offKeyUp(cb?)
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Useful on TV/desktop hosts where physical-key events matter.
|
|
290
|
+
|
|
291
|
+
### Storage (key/value)
|
|
292
|
+
|
|
293
|
+
Sub-module: `@lingxia/types/storage`
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
interface Storage {
|
|
297
|
+
get(key: string): unknown; // synchronous, untyped — cast as needed
|
|
298
|
+
set(key: string, value: unknown): void;
|
|
299
|
+
remove(key: string): void;
|
|
300
|
+
clear(): void; // wipes the whole namespace
|
|
301
|
+
keys(): string[]; // every key currently stored
|
|
302
|
+
has(key: string): boolean;
|
|
303
|
+
size(): number; // count of stored entries (not byte size)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const storage = lx.getStorage();
|
|
307
|
+
storage.set('lastSeenTip', 3);
|
|
308
|
+
if (!storage.has('userId')) await prompt();
|
|
309
|
+
for (const key of storage.keys()) console.log(key, storage.get(key));
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
All methods are **synchronous**. For larger or path-based storage, use the `FileManager` from `lx.getFileManager()` instead.
|
|
313
|
+
|
|
314
|
+
### `lx.app` — host-app metadata and control (`HostAppApi`)
|
|
315
|
+
|
|
316
|
+
```ts
|
|
317
|
+
interface HostAppApi {
|
|
318
|
+
readonly envVersion: 'developer' | 'preview' | 'release';
|
|
319
|
+
getBaseInfo(): AppBaseInfo;
|
|
320
|
+
checkUpdate(): Promise<HostAppUpdateCheckResult>;
|
|
321
|
+
exit(): void;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
interface AppBaseInfo {
|
|
325
|
+
language: string;
|
|
326
|
+
productName: string;
|
|
327
|
+
version: string;
|
|
328
|
+
SDKVersion: string;
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
**`envVersion`** — synchronous, fixed at app boot. Use to branch behavior:
|
|
333
|
+
|
|
334
|
+
```ts
|
|
335
|
+
if (lx.app.envVersion === 'developer') enableVerboseLogging();
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Configured via `lingxia build/dev/package --env <env>` and `lingxia.yaml` (`app.lingxiaServer`, `app.packageIdSuffix`) — see [App Project → Environment versions](../app/project.md#environment-versions). **Not** the same as the navigator-module `envVersion` (`'develop' | 'preview' | 'release'`) used in cross-lxapp URLs.
|
|
339
|
+
|
|
340
|
+
**`getBaseInfo()`** — language, product/host versions:
|
|
341
|
+
|
|
342
|
+
```ts
|
|
343
|
+
const info = lx.app.getBaseInfo();
|
|
344
|
+
console.log(info.productName, info.version, info.SDKVersion);
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
**`checkUpdate()`** — **home-lxapp only**. Non-home lxapps get a permission error. Calling this **opts the host app into custom update handling** for the process; LingXia's built-in update UI is suppressed afterward. Returns either `{ hasUpdate: false }` or `{ hasUpdate: true, update: HostAppUpdateInfo }` where `update.apply()` returns a `HostAppUpdateTask` (PromiseLike + AsyncIterable of progress events):
|
|
348
|
+
|
|
349
|
+
```ts
|
|
350
|
+
const result = await lx.app.checkUpdate();
|
|
351
|
+
if (result.hasUpdate) {
|
|
352
|
+
const task = result.update.apply();
|
|
353
|
+
for await (const event of task) {
|
|
354
|
+
// event.state: 'downloading' | 'downloaded' | 'installRequested' | 'failed'
|
|
355
|
+
// 'downloading' carries optional downloadedBytes and progress
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Direct package handoff is supported on Android and macOS today. Other platforms reject `apply()` with an unsupported-operation error — use `update.version` and `update.releaseNotes` to point users at the app store.
|
|
361
|
+
|
|
362
|
+
**`exit()`** — terminate the host app immediately. **No confirmation dialog** — show one yourself with `lx.showModal(...)` first if needed.
|
|
363
|
+
|
|
364
|
+
### `lx.surface` — dynamic surfaces (`SurfaceApi`)
|
|
365
|
+
|
|
366
|
+
This is the JS API for **opening surfaces dynamically at runtime** — distinct from the **declarative** `lingxia.yaml.ui.surfaces` in host config. Use this when you need to pop an overlay or open a secondary window from Logic.
|
|
367
|
+
|
|
368
|
+
```ts
|
|
369
|
+
interface SurfaceApi {
|
|
370
|
+
open(options: SurfaceOpenOptions): Promise<Surface>;
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
**Two kinds:**
|
|
375
|
+
|
|
376
|
+
| `kind` | Where | Notes |
|
|
377
|
+
|---|---|---|
|
|
378
|
+
| `'overlay'` | **all platforms** | A webview composited on top of the host activity. Use `position` + `size` for placement. |
|
|
379
|
+
| `'window'` | **macOS only** | A new native window. Mobile platforms reject with `surface_open_failed`. |
|
|
380
|
+
|
|
381
|
+
**Target — open a page from this lxapp, a page from another path, or an external URL:**
|
|
382
|
+
|
|
383
|
+
```ts
|
|
384
|
+
// open a registered page of this lxapp
|
|
385
|
+
await lx.surface.open({ kind: 'overlay', page: 'detail', query: { id: '42' } });
|
|
386
|
+
|
|
387
|
+
// open by raw path
|
|
388
|
+
await lx.surface.open({ kind: 'overlay', path: 'pages/detail/index', position: 'bottom', size: { height: '60%' } });
|
|
389
|
+
|
|
390
|
+
// open an external HTTPS URL (subject to trustedDomains)
|
|
391
|
+
await lx.surface.open({ kind: 'overlay', url: 'https://example.com/help' });
|
|
392
|
+
|
|
393
|
+
// open a window on macOS (rejected on mobile)
|
|
394
|
+
await lx.surface.open({ kind: 'window', page: 'settings', size: { width: 600, height: 400 } });
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
`OverlaySurfaceOptions.position`: `'center' | 'bottom' | 'left' | 'right' | 'top'`. `OverlaySurfaceSizeValue`: a positive number (absolute) or a `"N%"` template string (0 < N ≤ 100).
|
|
398
|
+
|
|
399
|
+
**Surface returned**: a stable handle for the opened page. The opener and the opened page can talk to each other:
|
|
400
|
+
|
|
401
|
+
```ts
|
|
402
|
+
interface Surface {
|
|
403
|
+
readonly id: string;
|
|
404
|
+
readonly kind: 'overlay' | 'window';
|
|
405
|
+
readonly visible: boolean; // tracks native show/hide events
|
|
406
|
+
readonly alive: boolean; // false after close()
|
|
407
|
+
|
|
408
|
+
postMessage(message: unknown): void;
|
|
409
|
+
onMessage(handler: (message: unknown) => void): () => void; // returns unsubscribe
|
|
410
|
+
|
|
411
|
+
show(): Promise<void>; // idempotent
|
|
412
|
+
hide(): Promise<void>; // hides without destroying — page state survives
|
|
413
|
+
close(): Promise<void>; // tears down the page instance
|
|
414
|
+
|
|
415
|
+
onShow(handler: (event: SurfaceVisibilityEvent) => void): () => void;
|
|
416
|
+
onHide(handler: (event: SurfaceVisibilityEvent) => void): () => void;
|
|
417
|
+
onClose(handler: (event: SurfaceClosedEvent) => void): () => void;
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
**Page side** — the opened page receives the opener's port via `lx.navigateTo`-style messaging or via the surface API. URL surfaces have no page-side receiver (you can't `postMessage` from arbitrary external HTML back into Logic).
|
|
422
|
+
|
|
423
|
+
**`hide()` vs `close()`**: hide preserves the page's JS state, scroll position, and form input — a subsequent `show()` restores everything. `close()` destroys it; `onClose` fires; further `show()` / `hide()` calls reject.
|
|
424
|
+
|
|
425
|
+
### `lx.env` — runtime paths (`LxEnv`)
|
|
426
|
+
|
|
427
|
+
```ts
|
|
428
|
+
interface LxEnv {
|
|
429
|
+
USER_DATA_PATH: string; // durable per-lxapp data root
|
|
430
|
+
USER_CACHE_PATH: string; // evictable per-lxapp cache root
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
Use these as roots when building `FileManager` paths or `downloadFile` targets. The storage-class model (when each path is auto-cleaned, size caps) is detailed in [`../reference/file-lifecycle.md`](../reference/file-lifecycle.md).
|
|
435
|
+
|
|
436
|
+
```ts
|
|
437
|
+
const profilePath = `${lx.env.USER_DATA_PATH}/profile.json`;
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### `lx.getLxAppInfo()` — manifest at runtime
|
|
441
|
+
|
|
442
|
+
Returns `LxAppInfo` with the lxapp's `appId`, `version`, `appName`, and other manifest fields. Useful for showing the user "you're on v1.2.3" or branching by `appId` when the same Logic is reused across embedded lxapps.
|
|
443
|
+
|
|
444
|
+
### Updates (lxapp self-update — distinct from `lx.app.checkUpdate`)
|
|
445
|
+
|
|
446
|
+
Sub-module: `@lingxia/types/update`. The **lxapp** update manager — different from `lx.app.checkUpdate()` (host app update). This one handles the runtime swapping a newer lxapp bundle into place:
|
|
447
|
+
|
|
448
|
+
```ts
|
|
449
|
+
interface UpdateManager {
|
|
450
|
+
applyUpdate(): void;
|
|
451
|
+
onUpdateReady(callback: (info: UpdateReadyInfo) => void): void;
|
|
452
|
+
onUpdateFailed(callback: (info: UpdateFailedInfo) => void): void;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const manager = lx.getUpdateManager();
|
|
456
|
+
manager.onUpdateReady(({ version, isForceUpdate, channel }) => {
|
|
457
|
+
// a new lxapp bundle is staged; channel: 'release' | 'preview' | 'developer'
|
|
458
|
+
manager.applyUpdate(); // restart into the new bundle
|
|
459
|
+
});
|
|
460
|
+
manager.onUpdateFailed(({ error, version }) => { /* log/report */ });
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## Calling native Rust routes from Logic
|
|
466
|
+
|
|
467
|
+
`lx.*` is the JS-only surface. For host-app-specific routes defined in Rust with `#[lingxia::native(...)]`, you call them from the **View** layer via the CLI-generated client at `@lingxia/native`. They are not on `lx`.
|
|
468
|
+
|
|
469
|
+
If you need cross-page business helpers callable from Logic as `lx.<yourNamespace>.foo(...)`, define a `lingxia::js` extension in the host Rust crate — see [`../native/development.md` → JS AppService Extensions](../native/development.md#js-appservice-extensions).
|
|
470
|
+
|
|
471
|
+
---
|
|
472
|
+
|
|
473
|
+
## Quick reference — finding a method
|
|
474
|
+
|
|
475
|
+
If you can't remember the exact name:
|
|
476
|
+
|
|
477
|
+
1. **Open `@lingxia/types/dist/index.d.ts`** in your editor and search the `interface Lx { … }` block.
|
|
478
|
+
2. **Or grep `node_modules/@lingxia/types`** for a keyword (`grep -r "scanCode" node_modules/@lingxia/types`).
|
|
479
|
+
3. The submodule structure (`@lingxia/types/media`, `/file`, `/ui`, …) groups option/result types — useful when typing your own helpers.
|
|
480
|
+
|
|
481
|
+
The `lx` interface is the authoritative source. This page is a routing index — when in doubt, read the `.d.ts`.
|