@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.
Files changed (37) hide show
  1. package/README.md +95 -0
  2. package/bin/install.mjs +247 -0
  3. package/package.json +49 -0
  4. package/scripts/sync.mjs +69 -0
  5. package/skill/SKILL.md +334 -0
  6. package/skill/app/apple-sdk.md +312 -0
  7. package/skill/app/applinks.md +289 -0
  8. package/skill/app/project.md +760 -0
  9. package/skill/cli/lxdev.md +195 -0
  10. package/skill/cli/reference.md +481 -0
  11. package/skill/examples/hello-host-js/README.md +25 -0
  12. package/skill/examples/hello-host-js/home/lxapp.json +12 -0
  13. package/skill/examples/hello-host-js/home/pages/home/index.json +4 -0
  14. package/skill/examples/hello-host-js/home/pages/home/index.ts +14 -0
  15. package/skill/examples/hello-host-js/home/pages/home/index.tsx +15 -0
  16. package/skill/examples/hello-host-js/lingxia.yaml +39 -0
  17. package/skill/examples/hello-host-rust/Cargo.toml +15 -0
  18. package/skill/examples/hello-host-rust/README.md +44 -0
  19. package/skill/examples/hello-host-rust/home/lxapp.json +13 -0
  20. package/skill/examples/hello-host-rust/home/pages/home/index.html +46 -0
  21. package/skill/examples/hello-host-rust/home/pages/home/index.json +4 -0
  22. package/skill/examples/hello-host-rust/lingxia.yaml +32 -0
  23. package/skill/examples/hello-host-rust/src/lib.rs +58 -0
  24. package/skill/examples/hello-lxapp/README.md +29 -0
  25. package/skill/examples/hello-lxapp/lxapp.config.ts +8 -0
  26. package/skill/examples/hello-lxapp/lxapp.json +14 -0
  27. package/skill/examples/hello-lxapp/package.json +14 -0
  28. package/skill/examples/hello-lxapp/pages/home/index.json +4 -0
  29. package/skill/examples/hello-lxapp/pages/home/index.ts +35 -0
  30. package/skill/examples/hello-lxapp/pages/home/index.tsx +34 -0
  31. package/skill/lxapp/bridge.md +654 -0
  32. package/skill/lxapp/components.md +375 -0
  33. package/skill/lxapp/guide.md +675 -0
  34. package/skill/lxapp/lx-api.md +481 -0
  35. package/skill/native/development.md +414 -0
  36. package/skill/reference/file-lifecycle.md +325 -0
  37. 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`.