@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
package/skill/SKILL.md ADDED
@@ -0,0 +1,334 @@
1
+ ---
2
+ name: lingxia
3
+ description: Build apps on the LingXia cross-platform framework — standalone lxapps (page-based mini-apps with a View+Logic split), native host apps (Android/iOS/macOS/Harmony shells embedding an lxapp), and Rust native extensions. TRIGGER on `lingxia` CLI, `lxapp`, `lingxia.yaml`, `lxapp.json`, `#[lingxia::native]`, `HostAddon`, `useLxPage`, `LxAppController`, or an lxapp-flavored `Page({})`. SKIP if the project imports `@tarojs/*`, `wx.*`, `uni-app`, `@dcloudio/*`, or `@remax/*` — those share the `Page({})` shape but are different runtimes. **Always read §"Step 0" before generating any file.**
4
+ license: MIT
5
+ allowed-tools: Read, Grep, Glob, Edit, Write, Bash(lingxia:*), Bash(npm:*), Bash(npx:*), Bash(test:*), Bash(ls:*), Bash(cat:*), Bash(cargo:*)
6
+ ---
7
+
8
+ # LingXia App Development
9
+
10
+ LingXia is a cross-platform app framework. This skill is the entry router — it carries the decision tree, fast-path recipes, and pointers into supporting reference files. **Read sub-files only when you need them**; do not load the whole tree up front.
11
+
12
+ This skill is installed via `npx @lingxia/skill install` (the `lingxia` CLI prints a hint pointing at that command after `lingxia new`, but does not install the skill itself). When you read this you can usually assume:
13
+
14
+ - The `lingxia` CLI is on `PATH` (verify with `lingxia --version`); if not, point the human at `docs/quick-start.md` in the LingXia repo for the install steps.
15
+ - You are inside a LingXia project — but **confirm the shape** with the probe in Step 0 rather than guessing.
16
+
17
+ For first-time CLI + platform-toolchain setup (one-time, human-facing onramp), the repo's `docs/quick-start.md` is the source. This skill does not duplicate it.
18
+
19
+ ---
20
+
21
+ ## Step 0 — Decide before scaffolding
22
+
23
+ **Always do this first.** Before generating a single file:
24
+
25
+ ### 0a. Identify what you're inside
26
+
27
+ ```bash
28
+ test -f lingxia.yaml && echo "host-app"
29
+ test -f lxapp.json && echo "lxapp"
30
+ test -f lxplugin.json && echo "lxplugin"
31
+ ```
32
+
33
+ If none match, you're about to scaffold a new project — continue to 0b. If one matches, jump to the fast-path for that shape.
34
+
35
+ ### 0b. Pick the shape (ask the user or infer)
36
+
37
+ 1. **Standalone lxapp or host app?** (A vs B/C below)
38
+ 2. **If host app:** which platforms? `android`, `ios`, `macos`, `harmony`, any combination.
39
+ 3. **If host app:** JS Logic for the home lxapp, or native-only Rust? (B vs C)
40
+ 4. **View framework:** React **or** Vue **or** HTML — pick **one**. The LingXia repo's `lingxia-showcase` example deliberately mixes all three; real apps do not.
41
+
42
+ Then scaffold:
43
+
44
+ ```bash
45
+ # Shape A — standalone lxapp
46
+ lingxia new my-lxapp -t lxapp -y
47
+
48
+ # Shape B/C — native host app
49
+ lingxia new my-app -t native-app -p macos --package-id com.example.myapp -y
50
+ # -p accepts: android, ios, macos, harmony, all (comma-separated)
51
+ ```
52
+
53
+ `lingxia doctor` verifies platform toolchains.
54
+
55
+ ---
56
+
57
+ ## What you build (pick one shape)
58
+
59
+ | Shape | What it is | Pick when |
60
+ |---|---|---|
61
+ | **A. Standalone lxapp** | Page-based mini-app that runs in any LingXia host (e.g. macOS Runner). | UI/page work, no native shell. |
62
+ | **B. Host app + JS lxapp** | Native installable app (Android/iOS/macOS/Harmony) embedding a home lxapp whose Logic is JS. | Most product apps. |
63
+ | **C. Host app + native Rust logic** | Same shell, but the home lxapp's Logic is in Rust. The lxapp is HTML-only with `logic: false`. | Native-only hosts (e.g. menu-bar utilities), or when the heavy lifting belongs in Rust. |
64
+
65
+ C is just B with `features.appService: false` and Rust replacing the JS Logic. You can also mix: a JS-Logic lxapp that **calls** Rust routes via `#[lingxia::native]` — that is still B, with native Rust as an *API surface* rather than the Logic layer.
66
+
67
+ ---
68
+
69
+ ## `@lingxia/*` npm packages at a glance
70
+
71
+ Every published package and what to import from each. Don't guess imports from the package name — use this table.
72
+
73
+ | Package | What it is | Imported by | Typical import |
74
+ |---|---|---|---|
75
+ | `@lingxia/react` | React hooks + framework-wrapped native components | lxapp View (React) | `useLxPage`, `useLxStream`, `useLxChannel`, `LxInput`, `LxVideo`, … |
76
+ | `@lingxia/vue` | Vue composables + framework-wrapped native components | lxapp View (Vue) | same surface as React, Vue-flavored |
77
+ | `@lingxia/html` | DOM helpers for HTML-only views (`subscribe`, `getActions`, …) | lxapp View (HTML) | `import { getActions, subscribe } from '@lingxia/html'` |
78
+ | `@lingxia/elements` | Pure-JS custom elements (`<lx-video>`, `<lx-input>`, …) | rarely direct — `@lingxia/react`/`vue` re-export wrappers around these | `registerVideoComponent`, `LxVideoElement` |
79
+ | `@lingxia/types` | **TypeScript declarations for the Logic-side `lx.*` API + `Page({})` / `App({})` globals** | lxapp Logic (`pages/*/index.ts`) | install as dev dep; types apply globally |
80
+ | `@lingxia/bridge` | Bridge runtime + low-level invocation helpers | rarely direct (advanced) | only when bypassing the framework wrappers |
81
+ | `@lingxia/native` | Virtual module — points at the **CLI-generated** native client (`#[lingxia::native]` routes) | lxapp View | `import { native } from '@lingxia/native'` — only after a native build runs |
82
+ | `@lingxia/page-runtime` | Internal — shared impl behind react/vue/html | **don't import directly** | — |
83
+ | `@lingxia/skill` | This skill itself | install via `npx @lingxia/skill install` | not imported in code |
84
+
85
+ **Logic-side typing.** Add `@lingxia/types` to the lxapp's `devDependencies` so `pages/*/index.ts` gets full intellisense for `lx.navigateTo(...)`, `lx.chooseMedia(...)`, the `Page({ data, onLoad, … })` shape, and so on. The declarations are global — no `import` needed in your Logic files.
86
+
87
+ For the full surface of `lx.*` and which namespace covers what, see [`./lxapp/lx-api.md`](./lxapp/lx-api.md).
88
+
89
+ ## Reference map (inside this skill)
90
+
91
+ | Need | File |
92
+ |---|---|
93
+ | Every CLI command, flag, env var (daily use) | [`./cli/reference.md`](./cli/reference.md) |
94
+ | Page authoring: `Page({})`, `useLxPage`, events | [`./lxapp/guide.md`](./lxapp/guide.md) |
95
+ | **Native components: `LxInput`, `LxVideo`, `LxMediaSwiper`, `LxPicker`, `LxNavigator`, `LxTextarea`** | [`./lxapp/components.md`](./lxapp/components.md) |
96
+ | **Logic-side `lx.*` API surface map** | [`./lxapp/lx-api.md`](./lxapp/lx-api.md) |
97
+ | Bridge mechanics: `setData`, stream, channel | [`./lxapp/bridge.md`](./lxapp/bridge.md) |
98
+ | Host project: `lingxia.yaml` reference, macOS App UI | [`./app/project.md`](./app/project.md) |
99
+ | Native Rust: `HostAddon`, `#[lingxia::native]`, facades, JS extensions | [`./native/development.md`](./native/development.md) |
100
+ | iOS/macOS SDK embedding, public startup APIs | [`./app/apple-sdk.md`](./app/apple-sdk.md) |
101
+ | Universal links / app links setup | [`./app/applinks.md`](./app/applinks.md) |
102
+ | File API lifecycle (storage classes, downloadFile, FileManager) | [`./reference/file-lifecycle.md`](./reference/file-lifecycle.md) |
103
+
104
+ ## Bundled hello-world examples
105
+
106
+ Three minimal end-to-end shapes ship with this skill — one per shape, named to make the JS-vs-Rust Logic split obvious. Read them first when you need to see the exact file layout for a shape. They are **not buildable starters** (no lockfiles, generated artifacts, or platform host scaffolding); they exist so you can match the shape and write real code with `lingxia new`.
107
+
108
+ | Example | Shape | Logic in | What it shows |
109
+ |---|---|---|---|
110
+ | [`./examples/hello-lxapp/`](./examples/hello-lxapp/README.md) | A — standalone lxapp | JS | `Page({})` Logic + React `useLxPage` View + `lxapp.json` security policy |
111
+ | [`./examples/hello-host-js/`](./examples/hello-host-js/README.md) | B — host + JS lxapp | JS | minimal macOS `lingxia.yaml` + embedded JS-Logic lxapp |
112
+ | [`./examples/hello-host-rust/`](./examples/hello-host-rust/README.md) | C — host + Rust Logic | **Rust** | `features.appService: false` + `lxapp.json` `"logic": false` + HTML view calling `window.native.*` |
113
+
114
+ `hello-host-js` and `hello-host-rust` share the host shell wiring (`lingxia.yaml` `ui`, `resources.bundles`, FFI export) — the diff is entirely on the Logic side: who owns state and how the View talks to it. Read both side-by-side when picking B vs C.
115
+
116
+ For a real, buildable starter, run `lingxia new` — the CLI emits a working project that matches the version on `PATH` and is regenerated per release.
117
+
118
+ **SDK-author internals** (bridge wire protocol, logging, webview lifecycle, env-version build-time plumbing) live in the LingXia repo under `docs/internal/` and `docs/draft/`. The skill summarizes the app-author-facing surface where relevant (e.g. env-version → [`./app/project.md`](./app/project.md#environment-versions)); the internal docs themselves are not needed for building on LingXia.
119
+
120
+ ---
121
+
122
+ ## Symptom router — error → file
123
+
124
+ Jump straight here when the user reports a concrete failure:
125
+
126
+ | Symptom | Where to look |
127
+ |---|---|
128
+ | `homeAppId` doesn't match any bundle / wrong app launches | [`./app/project.md`](./app/project.md) → `resources.bundles` |
129
+ | `fetch()` silently fails from an lxapp | [`./lxapp/guide.md`](./lxapp/guide.md) → "Security Policy" (`trustedDomains`) |
130
+ | "Is `fetch` / `setTimeout` / `URL` available in Logic?" | [`./lxapp/lx-api.md`](./lxapp/lx-api.md#standard-web-apis-built-in-globals) — yes, full Rong runtime |
131
+ | Need to read/write files (not just `lx.downloadFile`) | [`./lxapp/lx-api.md`](./lxapp/lx-api.md#file-and-transfer) → `lx.getFileManager()` |
132
+ | Need a scheduled task running across pages | [`./lxapp/lx-api.md`](./lxapp/lx-api.md#appservice-only-extras) → AppService `cron` |
133
+ | `attachPanel` validation rejected | [`./app/project.md`](./app/project.md) → "surfaces" rules |
134
+ | `setData` not reflecting in View | [`./lxapp/bridge.md`](./lxapp/bridge.md) → "How replication works" |
135
+ | Native route returns `BRIDGE_METHOD_NOT_FOUND` | [`./native/development.md`](./native/development.md) → Host Addon registration |
136
+ | `#[lingxia::native]` compiles but View can't call it | [`./native/development.md`](./native/development.md) → "Generated Native Client" |
137
+ | Stream cancels never trigger cleanup | [`./lxapp/bridge.md`](./lxapp/bridge.md) → `finally` block / `stream.on('cancel')` |
138
+ | `lingxia.yaml` change ignored after rebuild | [`./cli/reference.md`](./cli/reference.md) → `lingxia clean`, then rebuild |
139
+ | iOS dev app can't reach Mac dev server | [`./cli/reference.md`](./cli/reference.md) → `lingxia dev` (LAN reachability) |
140
+ | `Lingxia.initialize(...)` not found | [`./app/apple-sdk.md`](./app/apple-sdk.md) → use `Lingxia.quickStart()` (legacy removed) |
141
+ | TS doesn't know about `lx.foo()` / `Page({})` in Logic | install `@lingxia/types` as a devDependency; see [`./lxapp/lx-api.md`](./lxapp/lx-api.md) |
142
+ | `<LxVideo>` / `<LxPicker>` attribute not recognized by TS or runtime | [`./lxapp/components.md`](./lxapp/components.md) → component attribute table |
143
+ | Event handler on `LxVideo` fires DOM CustomEvent, not unwrapped detail | [`./lxapp/components.md`](./lxapp/components.md) → "Callback shapes by component" |
144
+
145
+ ---
146
+
147
+ ## Fast-path recipes
148
+
149
+ These cover the 80% case. For anything beyond, jump to the linked reference file.
150
+
151
+ ### Recipe 1 — A standalone lxapp page
152
+
153
+ **Logic** (`pages/home/index.ts`):
154
+
155
+ ```ts
156
+ Page({
157
+ data: { count: 0 },
158
+ onLoad(options) { /* options = URL query params */ },
159
+ increment() { this.setData({ count: this.data.count + 1 }); },
160
+ _privateHelper(n) { return n * 2; }, // _-prefixed = NOT exposed to View
161
+ });
162
+ ```
163
+
164
+ **View — React** (`pages/home/index.tsx`):
165
+
166
+ ```tsx
167
+ import { useLxPage } from '@lingxia/react';
168
+
169
+ type PageData = { count: number };
170
+ type PageActions = { increment: () => void };
171
+
172
+ export default function HomePage() {
173
+ const { data, actions } = useLxPage<PageData, PageActions>();
174
+ return <button onClick={() => actions.increment()}>{data.count}</button>;
175
+ }
176
+ ```
177
+
178
+ > **On the `?:` question.** The runtime guarantees `data` reflects Logic's initial `data` object by first paint, and `actions` is fully wired during page setup. Mark `PageData` / `PageActions` fields **required** unless your Logic genuinely populates them lazily (e.g. via an async `setData` after `onLoad`). Earlier drafts used all-`?` fields and produced unnecessary `actions.foo?.()` and `data?.x ?? default` noise — avoid that pattern.
179
+
180
+ **Page config** (`pages/home/index.json`):
181
+
182
+ ```json
183
+ { "navigationBarTitleText": "Home", "navigationStyle": "custom" }
184
+ ```
185
+
186
+ **Register** in `lxapp.json`:
187
+
188
+ ```json
189
+ { "pages": [{ "name": "home", "path": "pages/home/index" }] }
190
+ ```
191
+
192
+ **Run:** `lingxia dev` (launches macOS Runner).
193
+
194
+ Vue / HTML variants, action shapes (stream, channel), native components, `trustedDomains` security policy: [`./lxapp/guide.md`](./lxapp/guide.md). Bridge mechanics: [`./lxapp/bridge.md`](./lxapp/bridge.md).
195
+
196
+ ### Recipe 2 — A macOS host app window
197
+
198
+ **`lingxia.yaml`:**
199
+
200
+ ```yaml
201
+ app:
202
+ projectName: myapp
203
+ productName: My App
204
+ productVersion: 1.0.0
205
+ platforms: [macos]
206
+ homeAppId: my-home
207
+
208
+ macos:
209
+ bundleId: com.example.myapp
210
+ deploymentTarget: "12.0"
211
+ targetName: MyApp
212
+ executableName: MyApp
213
+
214
+ features: { appService: true, shell: false, devtools: false }
215
+
216
+ resources:
217
+ bundles:
218
+ - { type: lxapp, appId: my-home, path: my-home }
219
+
220
+ ui:
221
+ launch: { initialSurface: main }
222
+ surfaces:
223
+ - id: main
224
+ presentation: { kind: window }
225
+ content: { kind: lxapp, appId: my-home }
226
+ activators: []
227
+ ```
228
+
229
+ **Run:** `lingxia dev` (build, install, launch).
230
+
231
+ Menu-bar shape, sidebar + attachPanel shape, every section of `lingxia.yaml`, surfaces/activators rules, icon paths: [`./app/project.md`](./app/project.md).
232
+
233
+ ### Recipe 3 — A Rust native route called from the View
234
+
235
+ **Rust** (host crate `src/lib.rs`):
236
+
237
+ ```rust
238
+ use std::sync::Arc;
239
+
240
+ #[derive(serde::Deserialize)]
241
+ struct OpenInput { title: String }
242
+
243
+ // The #[lingxia::native(...)] attribute macro generates a sibling fn
244
+ // `pick_document_host()` that returns the host-entry registration value.
245
+ // You do NOT write `pick_document_host` yourself — it is macro-generated.
246
+ #[lingxia::native("editor.pickDocument")]
247
+ async fn pick_document(
248
+ app: Arc<lingxia::LxApp>, // optional, FIRST when present
249
+ input: OpenInput, // optional JSON payload
250
+ cancel: lingxia::host::HostCancel, // optional, LAST when present
251
+ ) -> lingxia::Result<String> {
252
+ let path = lingxia::app::state_file_for(&app, &format!("{}.md", input.title))?;
253
+ Ok(path.to_string_lossy().into_owned())
254
+ }
255
+
256
+ struct AppHostAddon;
257
+ impl lingxia::HostAddon for AppHostAddon {
258
+ fn install_host_apis(&self) {
259
+ // pick_document_host() is generated by the macro on `pick_document`.
260
+ lingxia::host::register_host_entry(pick_document_host());
261
+ }
262
+ fn start_services(&self) {}
263
+ }
264
+
265
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
266
+ #[unsafe(no_mangle)]
267
+ pub extern "C" fn lingxia_register_host_addon() {
268
+ lingxia::register_host_addon(Box::new(AppHostAddon));
269
+ }
270
+ ```
271
+
272
+ **Call from the View** (after a native build regenerates the client):
273
+
274
+ ```ts
275
+ import { native } from '@lingxia/native'; // module form, React/Vue
276
+ const path = await native.editor.pickDocument({ title: 'notes' });
277
+ ```
278
+
279
+ Streams, channels, JS AppService extensions, facade modules (`lingxia::app/task/file/media/update`), platform FFI for Android/Harmony, generated-client paths: [`./native/development.md`](./native/development.md).
280
+
281
+ ---
282
+
283
+ ## Where does this code go?
284
+
285
+ | Job | Lives in | Surface |
286
+ |---|---|---|
287
+ | UI rendering, page state | lxapp `pages/index.{tsx,vue,html}` | View |
288
+ | Page lifecycle, `setData`, action handlers (JS) | lxapp `pages/index.ts` | `Page({})` Logic |
289
+ | Cross-page business helpers callable as `lx.X(...)` | host Rust crate | `lingxia::js` extension (needs `standard` feature) |
290
+ | Page-scoped native UI (file/media picker, native browser) | host Rust crate | `#[lingxia::native]` route |
291
+ | Background services (devtool, push, ipc) | host Rust crate | `HostAddon::start_services` |
292
+ | Platform integrations needing predeclaration | `lingxia.yaml` | `capabilities`, `features` |
293
+ | Surfaces, panels, activators (macOS) | `lingxia.yaml` | `ui` |
294
+ | Bundled lxapp sources | folder + `resources.bundles` | `lingxia.yaml` |
295
+
296
+ ---
297
+
298
+ ## Top pitfalls (one per layer — full lists in the sub-files)
299
+
300
+ **LxApp** — see [`./lxapp/guide.md` → Common Pitfalls](./lxapp/guide.md#common-pitfalls):
301
+
302
+ - Generating `.tsx` + `.vue` + `.html` for one page. Pick **one** view framework per project.
303
+ - `fetch()` to a host not in `security.network.trustedDomains` fails silently.
304
+
305
+ **Host app** — see [`./app/project.md` → Common Pitfalls](./app/project.md#common-pitfalls):
306
+
307
+ - Editing generated `app.json` / `ui.json` instead of `lingxia.yaml`. They're regenerated every build.
308
+ - `homeAppId` not matching any `resources.bundles[].appId` — build fails or the wrong app launches.
309
+
310
+ **Native Rust** — see [`./native/development.md`](./native/development.md):
311
+
312
+ - Importing internal crates (`lingxia_logic`, `rong`) directly. Use `lingxia::*` facades.
313
+ - `app: Arc<LxApp>` not first, or `HostCancel` not last, in a `#[lingxia::native]` signature.
314
+
315
+ ---
316
+
317
+ ## Pre-ship checklist
318
+
319
+ **LxApp:**
320
+
321
+ - [ ] `lxapp.json` lists every page; `appId` set; `version` bumped if shipping.
322
+ - [ ] `security.network.trustedDomains` covers every external host (exact host names, no scheme/port/path).
323
+ - [ ] One view-framework file per page.
324
+ - [ ] Public actions typed in `PageActions`; private helpers prefixed `_`.
325
+ - [ ] `lingxia dev` runs cleanly.
326
+
327
+ **Host app:**
328
+
329
+ - [ ] `lingxia.yaml` validates: every required platform section present; `homeAppId` resolvable.
330
+ - [ ] `features.appService` matches the embedded lxapp's logic mode.
331
+ - [ ] All native routes return `lingxia::Result<T>` with `Serialize` outputs.
332
+ - [ ] `HostAddon` registers every route and extension.
333
+ - [ ] FFI exports for each target platform present.
334
+ - [ ] `lingxia doctor` passes; `lingxia dev` boots on a real/simulated device.
@@ -0,0 +1,312 @@
1
+ # Apple SDK Host Guide
2
+
3
+ > Scope: app-facing Apple SDK APIs for host apps on iOS and macOS.
4
+ > If a symbol is not documented here, it is not part of the supported host-app contract.
5
+
6
+ ## Integration Paths
7
+
8
+ Use one of two paths:
9
+
10
+ | If you are... | Use |
11
+ |---|---|
12
+ | Building a LingXia host app | `Lingxia.quickStart()` + `lingxia.yaml` |
13
+ | Embedding LingXia into an existing native app UI | `Lingxia.initializeRuntime()` + `LxAppController` + `LxAppHostView` |
14
+
15
+ Most apps should use `Lingxia.quickStart()`. Window shape, menu bar entries,
16
+ sidebar items, toolbar items, titlebar items, startup behavior, and the home
17
+ surface are configured in `lingxia.yaml`.
18
+
19
+ For `lingxia.yaml` configuration, see [App Project Configuration](../app/project.md).
20
+
21
+ ## Quick Start
22
+
23
+ Use `Lingxia.quickStart()` for product apps.
24
+
25
+ ```swift
26
+ import AppKit
27
+ import lingxia
28
+
29
+ class AppDelegate: NSObject, NSApplicationDelegate {
30
+ func applicationDidFinishLaunching(_ notification: Notification) {
31
+ Lingxia.enableWebViewDebugging()
32
+ do {
33
+ try Lingxia.quickStart()
34
+ } catch {
35
+ fatalError("Lingxia startup failed: \(error)")
36
+ }
37
+ }
38
+
39
+ func applicationShouldHandleReopen(
40
+ _ sender: NSApplication,
41
+ hasVisibleWindows flag: Bool
42
+ ) -> Bool {
43
+ return !Lingxia.handleAppActivation()
44
+ }
45
+
46
+ func applicationShouldTerminateAfterLastWindowClosed(
47
+ _ sender: NSApplication
48
+ ) -> Bool {
49
+ return false
50
+ }
51
+ }
52
+
53
+ let app = NSApplication.shared
54
+ let delegate = AppDelegate()
55
+ app.delegate = delegate
56
+ app.run()
57
+ ```
58
+
59
+ `quickStart()` loads bundled `app.json` and `ui.json`, initializes the runtime,
60
+ creates the host shell, installs configured activators, and opens
61
+ `ui.launch.initialSurface` when `openOnLaunch` is true.
62
+
63
+ `handleAppActivation()` forwards Dock/app activation to `ui.activators` entries
64
+ with `kind: appActivation`.
65
+
66
+ ## UI Configuration
67
+
68
+ Host UI belongs in `lingxia.yaml`, not in Swift code.
69
+
70
+ Examples:
71
+
72
+ - Hide sidebar and toolbar by omitting `sidebarItem` and `toolbarItem`.
73
+ - Create a normal window with a `window` surface.
74
+ - Create a menu bar monitor app with `statusPanel`, `menuBarItem`, and `openOnLaunch: false`.
75
+ - Add sidebar, toolbar, or titlebar actions with `sidebarItem`, `toolbarItem`, and `titlebarItem`.
76
+
77
+ See [macOS App UI](./project.md#macos-app-ui) for the full configuration model.
78
+
79
+ ## Advanced Embedding
80
+
81
+ Use this path only when an existing native app already owns its windows, scenes,
82
+ navigation, split views, panels, or layout, and LingXia should be mounted into
83
+ one native region.
84
+
85
+ ```swift
86
+ import AppKit
87
+ import lingxia
88
+
89
+ @MainActor
90
+ func mountLingXia(in containerView: NSView) async throws {
91
+ try Lingxia.initializeRuntime()
92
+
93
+ let controller = LxAppController()
94
+ Lingxia.activate(controller: controller)
95
+
96
+ let hostView = LxAppHostView(controller: controller)
97
+ hostView.translatesAutoresizingMaskIntoConstraints = false
98
+ containerView.addSubview(hostView)
99
+
100
+ NSLayoutConstraint.activate([
101
+ hostView.topAnchor.constraint(equalTo: containerView.topAnchor),
102
+ hostView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
103
+ hostView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
104
+ hostView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
105
+ ])
106
+
107
+ let session = try await controller.openHomeApp()
108
+ try await hostView.mount(session)
109
+ }
110
+ ```
111
+
112
+ Rules:
113
+
114
+ - Use one `LxAppController` per native integration flow.
115
+ - Use one `LxAppHostView` per embedded visual region.
116
+ - The host app owns native window/layout behavior.
117
+ - LingXia owns lxapp session lifecycle and webview attachment.
118
+
119
+ ## API Reference
120
+
121
+ ### `Lingxia`
122
+
123
+ ```swift
124
+ @MainActor
125
+ public enum Lingxia {
126
+ @discardableResult
127
+ public static func quickStart() throws -> LxAppShell
128
+
129
+ @available(*, deprecated, message: "Configure product UI in lingxia.yaml and use Lingxia.quickStart(). Use initializeRuntime() + LxAppController + LxAppHostView for advanced embedding.")
130
+ @discardableResult
131
+ public static func quickStart(
132
+ configuration: LxAppShellConfiguration
133
+ ) throws -> LxAppShell
134
+
135
+ public static func handleAppActivation() -> Bool
136
+
137
+ @discardableResult
138
+ public static func initializeRuntime() throws -> LxAppRuntimeInfo
139
+
140
+ public static func activate(controller: LxAppController)
141
+
142
+ public static func enableWebViewDebugging()
143
+
144
+ public static func handleAppLink(url: URL)
145
+ }
146
+ ```
147
+
148
+ `Lingxia.initialize()` has been removed. Use `quickStart()` for product apps or
149
+ `initializeRuntime()` for advanced embedding.
150
+
151
+ ### `LxAppController`
152
+
153
+ ```swift
154
+ @MainActor
155
+ public final class LxAppController {
156
+ public let id: LxAppControllerID
157
+ public private(set) var sessions: [LxAppSessionID: LxAppSession] { get }
158
+ public var events: AsyncStream<LxAppControllerEvent> { get }
159
+
160
+ public init()
161
+
162
+ public func setInterceptor(
163
+ _ kind: LxAppControllerInterceptor,
164
+ handler: ((LxAppInterceptContext) async -> LxAppInterceptDecision?)?
165
+ )
166
+
167
+ public func session(forAppId appId: String) -> LxAppSession?
168
+
169
+ @discardableResult
170
+ public func open(_ request: LxAppOpenRequest) async throws -> LxAppSession
171
+
172
+ @discardableResult
173
+ public func openHomeApp(path: String = "") async throws -> LxAppSession
174
+
175
+ public func navigate(_ request: LxAppNavigateRequest)
176
+
177
+ @discardableResult
178
+ public func close(_ sessionId: LxAppSessionID) async -> Bool
179
+ }
180
+ ```
181
+
182
+ Supporting types:
183
+
184
+ ```swift
185
+ public struct LxAppControllerID: Hashable, Codable, Sendable
186
+ public struct LxAppSessionID: Hashable, Codable, Sendable
187
+ public struct LxAppSession: Hashable, Codable, Sendable, Identifiable
188
+ public struct LxAppOpenRequest: Codable, Sendable
189
+ public struct LxAppNavigateRequest: Codable, Sendable
190
+ public enum LxAppOpenPresentation: String, Codable, Sendable, CaseIterable
191
+ public enum LxAppAnimation: String, Codable, Sendable, CaseIterable
192
+ public enum LxAppControllerEvent: Codable, Sendable
193
+ public enum LxAppControllerInterceptor: String, Codable, Sendable
194
+ public struct LxAppInterceptContext: Codable, Sendable
195
+ public enum LxAppInterceptDecision: Codable, Sendable
196
+ public struct LxAppErrorPayload: Codable, Sendable, Error, Hashable
197
+ public enum LxAppJSONValue: Codable, Sendable, Hashable
198
+ ```
199
+
200
+ Controller event semantics:
201
+
202
+ - `didOpen` carries the opened `LxAppSession`.
203
+ - `didClose` carries the closed `LxAppSession`.
204
+ - `.mountInHost(id:)` mounts the opened session into the registered `LxAppHostView`.
205
+
206
+ ### `LxAppHostView`
207
+
208
+ ```swift
209
+ #if os(iOS)
210
+ public typealias LxAppPlatformView = UIView
211
+ #else
212
+ public typealias LxAppPlatformView = NSView
213
+ #endif
214
+
215
+ @MainActor
216
+ public final class LxAppHostView: LxAppPlatformView {
217
+ public let id: LxAppHostViewID
218
+ public let controller: LxAppController
219
+ public private(set) var webView: WKWebView? { get }
220
+ public private(set) var mountedSession: LxAppSession? { get }
221
+ public private(set) var appId: String? { get }
222
+ public private(set) var currentPath: String? { get }
223
+ public private(set) var canGoBack: Bool { get }
224
+
225
+ public var events: AsyncStream<LxAppHostViewEvent> { get }
226
+
227
+ public init(controller: LxAppController, frame: CGRect = .zero)
228
+ public func attach(_ webView: WKWebView, appId: String?, path: String?)
229
+ public func mount(_ session: LxAppSession) async throws
230
+ public func mount(sessionId: LxAppSessionID) async throws
231
+ public func unmount()
232
+ public func dispatch(_ command: LxAppHostViewCommand)
233
+ }
234
+ ```
235
+
236
+ SwiftUI wrapper:
237
+
238
+ ```swift
239
+ public struct LxAppHostViewRepresentable {
240
+ public init(hostView: LxAppHostView, onEvent: ((LxAppHostViewEvent) -> Void)? = nil)
241
+ public init(controller: LxAppController, onEvent: ((LxAppHostViewEvent) -> Void)? = nil)
242
+ }
243
+ ```
244
+
245
+ Supporting types:
246
+
247
+ ```swift
248
+ public struct LxAppHostViewID: Hashable, Codable, Sendable
249
+ public enum LxAppHostViewEvent: Codable, Sendable
250
+ public enum LxAppHostViewCommand: Codable, Sendable
251
+ ```
252
+
253
+ Host-view event semantics:
254
+
255
+ - `didChangeTitle`, `didUpdateCanGoBack`, `didStartLoading`, `didFinishLoading`, and `didFail` come from the mounted webview.
256
+ - `dispatch(.triggerCapsuleAction(...))` forwards that action into the runtime for the mounted app session.
257
+
258
+ ### `LxAppRuntime`
259
+
260
+ ```swift
261
+ @MainActor
262
+ public final class LxAppRuntime {
263
+ public static let shared: LxAppRuntime
264
+ public private(set) var info: LxAppRuntimeInfo? { get }
265
+ public func initialize() throws -> LxAppRuntimeInfo
266
+ }
267
+
268
+ public struct LxAppRuntimeInfo: Codable, Sendable, Hashable {
269
+ public let homeAppId: String
270
+ public let capabilities: LxAppCapabilities
271
+ public let dataPath: String
272
+ public let cachesPath: String
273
+ }
274
+ ```
275
+
276
+ Most apps should not call `LxAppRuntime.shared.initialize()` directly. Use
277
+ `Lingxia.quickStart()` or `Lingxia.initializeRuntime()`.
278
+
279
+ ## Legacy Shell Override
280
+
281
+ `LxAppShellConfiguration`, `LxAppShell`, `LxAppSidebarMode`, and
282
+ `LxAppToolbarMode` remain available for migration and internal shell work.
283
+
284
+ New host apps should not use them to configure product UI. Put product UI in
285
+ `lingxia.yaml` instead.
286
+
287
+ Legacy examples:
288
+
289
+ ```swift
290
+ var config = LxAppShellConfiguration()
291
+ config.sidebar = .hidden
292
+ config.toolbar = .hidden
293
+ _ = try Lingxia.quickStart(configuration: config)
294
+ ```
295
+
296
+ Equivalent product configuration:
297
+
298
+ ```yaml
299
+ ui:
300
+ launch:
301
+ initialSurface: main
302
+ surfaces:
303
+ - id: main
304
+ presentation:
305
+ style: window
306
+ content:
307
+ kind: lxapp
308
+ appId: myapp
309
+ activators: []
310
+ ```
311
+
312
+ Prefer the YAML form for new apps.