peachsim 0.1.2204 → 0.1.2205

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 (84) hide show
  1. package/dist-lib/agent-daemon-client.cjs +1 -1
  2. package/dist-lib/agent-events.cjs +1 -1
  3. package/dist-lib/attached-projects.cjs +1 -1
  4. package/dist-lib/auth/shared-session.cjs +1 -1
  5. package/dist-lib/backend-origin.cjs +1 -1
  6. package/dist-lib/beta.cjs +1 -1
  7. package/dist-lib/beta.mjs +1 -1
  8. package/dist-lib/bridge-constants.cjs +1 -1
  9. package/dist-lib/bridge-contract-input.cjs +1 -1
  10. package/dist-lib/bridge-contract-input.mjs +1 -1
  11. package/dist-lib/bridge-contract.cjs +1 -1
  12. package/dist-lib/bridge-contract.mjs +1 -1
  13. package/dist-lib/bundle-channel.cjs +18 -10
  14. package/dist-lib/capture-contract.cjs +1 -1
  15. package/dist-lib/capture-contract.mjs +1 -1
  16. package/dist-lib/cloud-contract.cjs +1 -1
  17. package/dist-lib/cloud-contract.mjs +1 -1
  18. package/dist-lib/cloud-test-args.cjs +1 -1
  19. package/dist-lib/cloud-test-args.mjs +1 -1
  20. package/dist-lib/cloud.cjs +1 -1
  21. package/dist-lib/cloud.mjs +1 -1
  22. package/dist-lib/config.cjs +1 -1
  23. package/dist-lib/config.mjs +1 -1
  24. package/dist-lib/detox/index.cjs +1 -1
  25. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  26. package/dist-lib/expo-swift-bundle.cjs +1 -1
  27. package/dist-lib/expo-swift-metadata.cjs +1 -1
  28. package/dist-lib/expo-swift-metadata.mjs +1 -1
  29. package/dist-lib/home-paths.cjs +1 -1
  30. package/dist-lib/host/bridge-host.cjs +18 -10
  31. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  32. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  33. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  34. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  35. package/dist-lib/host/websocket-proxy.cjs +1 -1
  36. package/dist-lib/index.cjs +1 -1
  37. package/dist-lib/jump-to-source-babel.cjs +1 -1
  38. package/dist-lib/jump-to-source-native.cjs +1 -1
  39. package/dist-lib/kotlin-host.cjs +18 -10
  40. package/dist-lib/menu.cjs +1 -1
  41. package/dist-lib/menu.mjs +1 -1
  42. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  43. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  44. package/dist-lib/metro-production-bundle.cjs +1 -1
  45. package/dist-lib/metro-production-bundle.mjs +1 -1
  46. package/dist-lib/metro.cjs +1 -1
  47. package/dist-lib/native-app-identity-project.cjs +1 -1
  48. package/dist-lib/native-app-identity-project.mjs +1 -1
  49. package/dist-lib/native-app-identity.cjs +1 -1
  50. package/dist-lib/native-app-identity.mjs +1 -1
  51. package/dist-lib/native-firebase-metadata.cjs +1 -1
  52. package/dist-lib/native-firebase-metadata.mjs +1 -1
  53. package/dist-lib/pdfium-raster.cjs +1 -1
  54. package/dist-lib/pdfium-raster.mjs +1 -1
  55. package/dist-lib/plist.cjs +1 -1
  56. package/dist-lib/plist.mjs +1 -1
  57. package/dist-lib/profiles.cjs +1 -1
  58. package/dist-lib/public-brand.cjs +1 -1
  59. package/dist-lib/public-origins-lib-esm.json +24 -24
  60. package/dist-lib/public-origins-lib.json +53 -53
  61. package/dist-lib/react-native-host-modules.cjs +1 -1
  62. package/dist-lib/react-native-host-modules.mjs +1 -1
  63. package/dist-lib/reap-browser.cjs +1 -1
  64. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  65. package/dist-lib/sdk.cjs +1 -1
  66. package/dist-lib/sdk.mjs +1 -1
  67. package/dist-lib/skills.cjs +38 -18
  68. package/dist-lib/swift-compile.cjs +1 -1
  69. package/dist-lib/swift-compile.mjs +1 -1
  70. package/dist-lib/swift-package.cjs +1 -1
  71. package/dist-lib/swift-package.mjs +1 -1
  72. package/dist-lib/swift-submit.cjs +1 -1
  73. package/dist-lib/swift-submit.mjs +1 -1
  74. package/dist-lib/swift-toolchain.cjs +1 -1
  75. package/dist-lib/swift.cjs +1 -1
  76. package/dist-lib/vite-swift.cjs +1 -1
  77. package/dist-lib/vite.cjs +1 -1
  78. package/dist-lib/worklet-executable.cjs +1 -1
  79. package/dist-lib/worklet-executable.mjs +1 -1
  80. package/kotlin/sdk.tar.gz +0 -0
  81. package/package.json +1 -1
  82. package/src/android-drawables.ts +1 -1
  83. package/src/android-layout-ids.ts +34 -11
  84. package/src/kotlin-host.ts +20 -9
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2204 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2205 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
package/dist-lib/sdk.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2204 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2205 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
package/dist-lib/sdk.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2204 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2205 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2204 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2205 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -96945,7 +96945,7 @@ async function kotlinAppArtifact(app, moduleRoot, origin) {
96945
96945
  "",
96946
96946
  "@JsExport",
96947
96947
  "fun peachAppMount(density: Float, fontScale: Float, locale: String,",
96948
- " font: String, insets: String, night: Boolean,",
96948
+ " font: String, insets: String, night: Boolean, orientation: Int,",
96949
96949
  " invalidate: (Boolean) -> Unit): String = dev.peach.app.peachStartApp(",
96950
96950
  " dev.peach.app.PeachAppSpec(",
96951
96951
  " dev.peach.app.compiledAppResources,",
@@ -96962,7 +96962,7 @@ async function kotlinAppArtifact(app, moduleRoot, origin) {
96962
96962
  " },",
96963
96963
  ` ${app.predictiveBack},`,
96964
96964
  " ),",
96965
- " density, fontScale, locale, font, insets, night, invalidate,",
96965
+ " density, fontScale, locale, font, insets, night, orientation, invalidate,",
96966
96966
  ")",
96967
96967
  "",
96968
96968
  "@JsExport",
@@ -97057,15 +97057,18 @@ ${drawable.files.map((file) => drawableInit(file, import_node_path125.default.re
97057
97057
  )`
97058
97058
  }))
97059
97059
  },
97060
- // a layout is its element tree; one only in a qualified directory has none
97060
+ // a layout is the element tree of each file the device API can load, with
97061
+ // its directory's qualifiers
97061
97062
  {
97062
97063
  type: "layout",
97063
97064
  table: "peachAppLayouts",
97064
- value: "LayoutNode?",
97065
- entries: app.layoutIds.layouts.map((name3) => {
97066
- const element = app.layoutIds.elements.get(name3);
97067
- return { name: name3, init: element ? layoutInit(element) : "null" };
97068
- })
97065
+ value: "List<LayoutVariant>",
97066
+ entries: app.layoutIds.layouts.map((name3) => ({
97067
+ name: name3,
97068
+ init: `listOf(${(app.layoutIds.elements.get(name3) ?? []).map(
97069
+ (variant2) => `LayoutVariant(${variant2.orientation}, ${variant2.night}, ${variant2.version}, ${layoutInit(variant2.element)})`
97070
+ ).join(", ")})`
97071
+ }))
97069
97072
  },
97070
97073
  // the day and the night ARGB
97071
97074
  {
@@ -97087,7 +97090,11 @@ ${drawable.files.map((file) => drawableInit(file, import_node_path125.default.re
97087
97090
  }))
97088
97091
  });
97089
97092
  const kinds = tableKinds.map((kind, index) => numbered(kind, index));
97090
- const viewClasses = layoutViewClasses(app.layoutIds.elements.values());
97093
+ const viewClasses = layoutViewClasses(
97094
+ [...app.layoutIds.elements.values()].flatMap(
97095
+ (variants) => variants.map((variant2) => variant2.element)
97096
+ )
97097
+ );
97091
97098
  const rKinds = [
97092
97099
  ...kinds,
97093
97100
  ...nameOnly.map(
@@ -97126,6 +97133,7 @@ ${drawable.files.map((file) => drawableInit(file, import_node_path125.default.re
97126
97133
  "androidx.compose.ui.unit.Dp",
97127
97134
  "android.content.Context",
97128
97135
  "android.util.AttributeSet",
97136
+ "android.content.res.LayoutVariant",
97129
97137
  "android.view.LayoutNode",
97130
97138
  "android.view.View"
97131
97139
  ].map((name3) => `import ${name3}`),
@@ -115932,16 +115940,27 @@ function readAndroidLayoutIds(moduleDir) {
115932
115940
  const elements = /* @__PURE__ */ new Map();
115933
115941
  for (const dir of import_node_fs143.default.existsSync(res) ? import_node_fs143.default.readdirSync(res).sort() : []) {
115934
115942
  const directory2 = import_node_path145.default.join(res, dir);
115935
- if (LAYOUT_DIR.test(dir)) {
115943
+ if (dir === "layout" || dir.startsWith("layout-")) {
115944
+ const match = LAYOUT_DIR.exec(dir);
115945
+ if (!match)
115946
+ throw new Error(
115947
+ `${res}/${dir}: layouts under this qualifier are not resolved by Peach yet`
115948
+ );
115949
+ const orientation = match[1] === "port" ? 1 : match[1] === "land" ? 2 : 0;
115950
+ const night = match[2] === "notnight" ? 1 : match[2] === "night" ? 2 : 0;
115951
+ const version2 = Number(match[3] ?? 0);
115936
115952
  for (const entry of import_node_fs143.default.readdirSync(directory2).sort()) {
115937
115953
  if (!entry.endsWith(".xml")) continue;
115938
- layouts.add(entry.slice(0, -".xml".length));
115954
+ const name3 = entry.slice(0, -".xml".length);
115955
+ layouts.add(name3);
115939
115956
  const xml = import_node_fs143.default.readFileSync(import_node_path145.default.join(directory2, entry), "utf8");
115940
- for (const match of xml.matchAll(ID_REFERENCE)) ids.add(match[1]);
115941
- if (dir === "layout") {
115942
- const root = parseXml(xml, import_node_path145.default.join(directory2, entry)).children[0];
115943
- if (root) elements.set(entry.slice(0, -".xml".length), root);
115944
- }
115957
+ for (const reference of xml.matchAll(ID_REFERENCE)) ids.add(reference[1]);
115958
+ const element = parseXml(xml, import_node_path145.default.join(directory2, entry)).children[0];
115959
+ if (!element || version2 > DEVICE_API) continue;
115960
+ elements.set(name3, [
115961
+ ...elements.get(name3) ?? [],
115962
+ { orientation, night, version: version2, element }
115963
+ ]);
115945
115964
  }
115946
115965
  } else if (dir.startsWith("values")) {
115947
115966
  for (const entry of import_node_fs143.default.readdirSync(directory2).sort()) {
@@ -115963,8 +115982,9 @@ var init_android_layout_ids = __esm({
115963
115982
  init_define_SWIFT_COVERAGE_SOURCE_DIGESTS();
115964
115983
  import_node_fs143 = __toESM(require("node:fs"), 1);
115965
115984
  import_node_path145 = __toESM(require("node:path"), 1);
115985
+ init_android_drawables();
115966
115986
  init_android_xml();
115967
- LAYOUT_DIR = /^layout(?:-[A-Za-z0-9+-]+)?$/;
115987
+ LAYOUT_DIR = /^layout(?:-(port|land))?(?:-(notnight|night))?(?:-v(\d+))?$/;
115968
115988
  ID_REFERENCE = /@\+?id\/([A-Za-z0-9_.]+)/g;
115969
115989
  ID_ITEM = /<item\b[^>]*\btype\s*=\s*"id"[^>]*>/g;
115970
115990
  ITEM_NAME = /\bname\s*=\s*"([^"]+)"/;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2204 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2205 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2204 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2205 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
 
3
3
  // src/swift-compile.ts
4
4
  import {
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2204 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2205 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2204 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2205 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
 
3
3
  // src/swift-package.ts
4
4
  function swiftManifestSettings(target) {
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2204 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2205 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2204 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2205 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
 
3
3
  // src/is-record.ts
4
4
  function isRecord(value) {
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2204 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2205 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2204 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2205 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2204 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2205 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
package/dist-lib/vite.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2204 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2205 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2204 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2205 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2204 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2205 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
 
3
3
  // src/worklet-executable.ts
4
4
  import { parse } from "acorn";
package/kotlin/sdk.tar.gz CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "peachsim",
3
- "version": "0.1.2204",
3
+ "version": "0.1.2205",
4
4
  "description": "Vite and Metro plugins, testing drivers, and SDK exports for peach.",
5
5
  "author": "Tamagui LLC",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -75,7 +75,7 @@ export interface AndroidDrawable {
75
75
  }
76
76
 
77
77
  // the device the oracle measures runs API 37
78
- const DEVICE_API = 37
78
+ export const DEVICE_API = 37
79
79
  // ResTable_config's density values, as the resource table records them
80
80
  const BUCKETS: Record<string, number> = {
81
81
  ldpi: 120,
@@ -1,5 +1,6 @@
1
1
  import fs from 'node:fs'
2
2
  import path from 'node:path'
3
+ import { DEVICE_API } from './android-drawables'
3
4
  import { parseXml, type XmlElement } from './android-xml'
4
5
 
5
6
  // the layout names and view ids a module's R class declares: every layout
@@ -8,11 +9,22 @@ import { parseXml, type XmlElement } from './android-xml'
8
9
  export interface AndroidLayoutIds {
9
10
  layouts: string[]
10
11
  ids: string[]
11
- // the unqualified `layout` directory's files by name, which Resources inflates
12
- elements: Map<string, XmlElement>
12
+ // each layout's files the device API can load, which Resources chooses
13
+ // between for the configuration
14
+ elements: Map<string, LayoutVariant[]>
13
15
  }
14
16
 
15
- const LAYOUT_DIR = /^layout(?:-[A-Za-z0-9+-]+)?$/
17
+ // a layout directory's qualifiers as ResTable_config values: orientation 1
18
+ // port, 2 land; night 1 notnight, 2 night; 0 leaves either unset
19
+ export interface LayoutVariant {
20
+ orientation: number
21
+ night: number
22
+ version: number
23
+ element: XmlElement
24
+ }
25
+
26
+ // the qualifiers Peach resolves for layouts, in aapt2's required order
27
+ const LAYOUT_DIR = /^layout(?:-(port|land))?(?:-(notnight|night))?(?:-v(\d+))?$/
16
28
  const ID_REFERENCE = /@\+?id\/([A-Za-z0-9_.]+)/g
17
29
  const ID_ITEM = /<item\b[^>]*\btype\s*=\s*"id"[^>]*>/g
18
30
  const ITEM_NAME = /\bname\s*=\s*"([^"]+)"/
@@ -21,19 +33,30 @@ export function readAndroidLayoutIds(moduleDir: string): AndroidLayoutIds {
21
33
  const res = path.join(moduleDir, 'src/main/res')
22
34
  const layouts = new Set<string>()
23
35
  const ids = new Set<string>()
24
- const elements = new Map<string, XmlElement>()
36
+ const elements = new Map<string, LayoutVariant[]>()
25
37
  for (const dir of fs.existsSync(res) ? fs.readdirSync(res).sort() : []) {
26
38
  const directory = path.join(res, dir)
27
- if (LAYOUT_DIR.test(dir)) {
39
+ if (dir === 'layout' || dir.startsWith('layout-')) {
40
+ const match = LAYOUT_DIR.exec(dir)
41
+ if (!match)
42
+ throw new Error(
43
+ `${res}/${dir}: layouts under this qualifier are not resolved by Peach yet`,
44
+ )
45
+ const orientation = match[1] === 'port' ? 1 : match[1] === 'land' ? 2 : 0
46
+ const night = match[2] === 'notnight' ? 1 : match[2] === 'night' ? 2 : 0
47
+ const version = Number(match[3] ?? 0)
28
48
  for (const entry of fs.readdirSync(directory).sort()) {
29
49
  if (!entry.endsWith('.xml')) continue
30
- layouts.add(entry.slice(0, -'.xml'.length))
50
+ const name = entry.slice(0, -'.xml'.length)
51
+ layouts.add(name)
31
52
  const xml = fs.readFileSync(path.join(directory, entry), 'utf8')
32
- for (const match of xml.matchAll(ID_REFERENCE)) ids.add(match[1])
33
- if (dir === 'layout') {
34
- const root = parseXml(xml, path.join(directory, entry)).children[0]
35
- if (root) elements.set(entry.slice(0, -'.xml'.length), root)
36
- }
53
+ for (const reference of xml.matchAll(ID_REFERENCE)) ids.add(reference[1])
54
+ const element = parseXml(xml, path.join(directory, entry)).children[0]
55
+ if (!element || version > DEVICE_API) continue
56
+ elements.set(name, [
57
+ ...(elements.get(name) ?? []),
58
+ { orientation, night, version, element },
59
+ ])
37
60
  }
38
61
  } else if (dir.startsWith('values')) {
39
62
  for (const entry of fs.readdirSync(directory).sort()) {
@@ -865,7 +865,7 @@ export async function kotlinAppArtifact(
865
865
  '',
866
866
  '@JsExport',
867
867
  'fun peachAppMount(density: Float, fontScale: Float, locale: String,',
868
- ' font: String, insets: String, night: Boolean,',
868
+ ' font: String, insets: String, night: Boolean, orientation: Int,',
869
869
  ' invalidate: (Boolean) -> Unit): String = dev.peach.app.peachStartApp(',
870
870
  ' dev.peach.app.PeachAppSpec(',
871
871
  ' dev.peach.app.compiledAppResources,',
@@ -882,7 +882,7 @@ export async function kotlinAppArtifact(
882
882
  ' },',
883
883
  ` ${app.predictiveBack},`,
884
884
  ' ),',
885
- ' density, fontScale, locale, font, insets, night, invalidate,',
885
+ ' density, fontScale, locale, font, insets, night, orientation, invalidate,',
886
886
  ')',
887
887
  '',
888
888
  '@JsExport',
@@ -1009,15 +1009,21 @@ function appResourceSources(app: KotlinAppProject): Record<string, string> {
1009
1009
  .join(',\n')}\n)`,
1010
1010
  })),
1011
1011
  },
1012
- // a layout is its element tree; one only in a qualified directory has none
1012
+ // a layout is the element tree of each file the device API can load, with
1013
+ // its directory's qualifiers
1013
1014
  {
1014
1015
  type: 'layout',
1015
1016
  table: 'peachAppLayouts',
1016
- value: 'LayoutNode?',
1017
- entries: app.layoutIds.layouts.map((name) => {
1018
- const element = app.layoutIds.elements.get(name)
1019
- return { name, init: element ? layoutInit(element) : 'null' }
1020
- }),
1017
+ value: 'List<LayoutVariant>',
1018
+ entries: app.layoutIds.layouts.map((name) => ({
1019
+ name,
1020
+ init: `listOf(${(app.layoutIds.elements.get(name) ?? [])
1021
+ .map(
1022
+ (variant) =>
1023
+ `LayoutVariant(${variant.orientation}, ${variant.night}, ${variant.version}, ${layoutInit(variant.element)})`,
1024
+ )
1025
+ .join(', ')})`,
1026
+ })),
1021
1027
  },
1022
1028
  // the day and the night ARGB
1023
1029
  {
@@ -1045,7 +1051,11 @@ function appResourceSources(app: KotlinAppProject): Record<string, string> {
1045
1051
  })),
1046
1052
  })
1047
1053
  const kinds = tableKinds.map((kind, index) => numbered(kind, index))
1048
- const viewClasses = layoutViewClasses(app.layoutIds.elements.values())
1054
+ const viewClasses = layoutViewClasses(
1055
+ [...app.layoutIds.elements.values()].flatMap((variants) =>
1056
+ variants.map((variant) => variant.element),
1057
+ ),
1058
+ )
1049
1059
  const rKinds = [
1050
1060
  ...kinds,
1051
1061
  ...nameOnly.map((kind, index) =>
@@ -1084,6 +1094,7 @@ function appResourceSources(app: KotlinAppProject): Record<string, string> {
1084
1094
  'androidx.compose.ui.unit.Dp',
1085
1095
  'android.content.Context',
1086
1096
  'android.util.AttributeSet',
1097
+ 'android.content.res.LayoutVariant',
1087
1098
  'android.view.LayoutNode',
1088
1099
  'android.view.View',
1089
1100
  ].map((name) => `import ${name}`),