peachsim 0.1.666 → 0.1.667

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 (75) 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/agent-identity.cjs +1 -1
  4. package/dist-lib/agent-sessions.cjs +1 -1
  5. package/dist-lib/attached-projects.cjs +1 -1
  6. package/dist-lib/auth/shared-session.cjs +1 -1
  7. package/dist-lib/backend-origin.cjs +1 -1
  8. package/dist-lib/beta.cjs +1 -1
  9. package/dist-lib/beta.mjs +1 -1
  10. package/dist-lib/bridge-constants.cjs +1 -1
  11. package/dist-lib/bridge-contract-input.cjs +1 -1
  12. package/dist-lib/bridge-contract-input.mjs +1 -1
  13. package/dist-lib/bridge-contract.cjs +1 -1
  14. package/dist-lib/bridge-contract.mjs +1 -1
  15. package/dist-lib/capture-contract.cjs +1 -1
  16. package/dist-lib/capture-contract.mjs +1 -1
  17. package/dist-lib/cli-constants.cjs +1 -1
  18. package/dist-lib/cloud-contract.cjs +1 -1
  19. package/dist-lib/cloud-contract.mjs +1 -1
  20. package/dist-lib/cloud-test-args.cjs +1 -1
  21. package/dist-lib/cloud-test-args.mjs +1 -1
  22. package/dist-lib/cloud.cjs +1 -1
  23. package/dist-lib/cloud.mjs +1 -1
  24. package/dist-lib/config.cjs +1 -1
  25. package/dist-lib/detox/index.cjs +1 -1
  26. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  27. package/dist-lib/home-paths.cjs +1 -1
  28. package/dist-lib/host/bridge-host.cjs +1 -1
  29. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  30. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  31. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  32. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  33. package/dist-lib/host/websocket-proxy.cjs +1 -1
  34. package/dist-lib/index.cjs +1 -1
  35. package/dist-lib/jump-to-source-babel.cjs +1 -1
  36. package/dist-lib/jump-to-source-native.cjs +1 -1
  37. package/dist-lib/kotlin-host.cjs +4 -1
  38. package/dist-lib/menu.cjs +1 -1
  39. package/dist-lib/menu.mjs +1 -1
  40. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  41. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  42. package/dist-lib/metro-production-bundle.cjs +1 -1
  43. package/dist-lib/metro-production-bundle.mjs +1 -1
  44. package/dist-lib/metro.cjs +1 -1
  45. package/dist-lib/native-app-identity.cjs +1 -1
  46. package/dist-lib/native-app-identity.mjs +1 -1
  47. package/dist-lib/plist.cjs +1 -1
  48. package/dist-lib/plist.mjs +1 -1
  49. package/dist-lib/profiles.cjs +1 -1
  50. package/dist-lib/public-brand.cjs +1 -1
  51. package/dist-lib/react-native-host-modules.cjs +1 -1
  52. package/dist-lib/react-native-host-modules.mjs +1 -1
  53. package/dist-lib/render-mode.cjs +1 -1
  54. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  55. package/dist-lib/sdk.cjs +1 -1
  56. package/dist-lib/sdk.mjs +1 -1
  57. package/dist-lib/skills.cjs +1 -1
  58. package/dist-lib/swift-compile.cjs +1 -1
  59. package/dist-lib/swift-compile.mjs +1 -1
  60. package/dist-lib/swift-package.cjs +1 -1
  61. package/dist-lib/swift-package.mjs +1 -1
  62. package/dist-lib/swift-submit.cjs +1 -1
  63. package/dist-lib/swift-submit.mjs +1 -1
  64. package/dist-lib/swift.cjs +1 -1
  65. package/dist-lib/vite-swift.cjs +1 -1
  66. package/dist-lib/vite.cjs +1 -1
  67. package/kotlin/AppSources/dev/peach/app/PeachApp.kt +15 -3
  68. package/kotlin/ComposeSources/PeachComposeHost.kt +200 -0
  69. package/kotlin/Sources/org/json/JSON.kt +13 -0
  70. package/kotlin/fixtures/kotlin-app-variants/Anim.kt +62 -0
  71. package/kotlin/fixtures/kotlin-app-variants/Form.kt +47 -0
  72. package/kotlin/fixtures/kotlin-app-variants/Ime.kt +51 -0
  73. package/kotlin/fixtures/kotlin-app-variants/Note.kt +40 -0
  74. package/package.json +1 -1
  75. package/src/kotlin-host.ts +3 -0
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
package/dist-lib/beta.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
package/dist-lib/beta.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/beta.ts
4
4
  var IS_BETA = true;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/bridge-contract.ts
4
4
  var SIM_LONG_PRESS_MAX_MS = 5e3;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/bridge-contract.ts
4
4
  var REFUSED_PERFORM_STEP_TYPES = [
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/capture-contract.ts
4
4
  var RNX_SCREEN_CAPTURE_VERSION = 1;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/cloud-contract.ts
4
4
  var RNX_CLOUD_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/cloud-test-args.ts
4
4
  var CLOUD_TEST_ARGS_MAX_COUNT = 32;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (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
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/host/fetch-proxy-overrides.ts
4
4
  var FETCH_PROXY_BROWSER_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -420,6 +420,9 @@ async function kotlinAppArtifact(app, moduleRoot, origin) {
420
420
  "",
421
421
  "@JsExport",
422
422
  "fun peachAppNight(night: Boolean) = dev.peach.app.peachAppNight(night)",
423
+ "",
424
+ "@JsExport",
425
+ "fun peachAppIme(bottom: Int) = dev.peach.app.peachAppIme(bottom)",
423
426
  ""
424
427
  ].join("\n");
425
428
  const generated = {
package/dist-lib/menu.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
package/dist-lib/menu.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/public-brand.ts
4
4
  var name = "Peach";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (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
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/metro-production-bundle.ts
4
4
  var RNXSIM_METRO_MODULE_PATHS_PREFIX = "globalThis.__sootsimModulePaths=";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/native-app-identity.ts
4
4
  var NATIVE_APP_IDENTITY_PREFIX = "globalThis.__sootsimNativeAppIdentity=";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/plist.ts
4
4
  import plistModule from "@expo/plist";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/react-native-host-modules.ts
4
4
  var REACT_NATIVE_PASSTHROUGH_SPECIFIERS = [
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
package/dist-lib/sdk.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
package/dist-lib/sdk.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (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
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/swift-compile.ts
4
4
  import { execFile as execFile2 } from "node:child_process";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/swift-package.ts
4
4
  function isSwiftManifestFile(file) {
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/swift-submit.ts
4
4
  var SWIFT_COMPILE_PATH = "/api/swift-compile";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
package/dist-lib/vite.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.666 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.667 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -68,8 +68,9 @@ internal fun peachApplication(): Application = runningApp().application
68
68
  // them on an edge-to-edge window, none once the decor fits system windows.
69
69
  // insets lists the status bars, navigation bars, display cutout, system
70
70
  // gestures, mandatory system gestures and tappable element, each left, top,
71
- // right and bottom.
72
- private class DeviceInsets(private val window: Window, insets: String) : PlatformWindowInsets {
71
+ // right and bottom. the IME's inset is the keyboard's height above the
72
+ // screen's bottom edge, which moves with the keyboard frame by frame.
73
+ private class DeviceInsets(private val window: Window, insets: String, imeBottom: () -> Int) : PlatformWindowInsets {
73
74
  private val pixels = insets.split(',').map { it.toInt() }.also { require(it.size == 24) }
74
75
 
75
76
  // read when compose lays out, so a later change to the window applies
@@ -84,6 +85,7 @@ private class DeviceInsets(private val window: Window, insets: String) : Platfor
84
85
  override val systemGestures = type(3)
85
86
  override val mandatorySystemGestures = type(4)
86
87
  override val tappableElement = type(5)
88
+ override val ime = PlatformInsets({ 0 }, { 0 }, { 0 }, { if (window.fitsSystemWindows) 0 else imeBottom() })
87
89
  override val systemBars = PlatformInsets(
88
90
  { maxOf(statusBars.left, navigationBars.left) },
89
91
  { maxOf(statusBars.top, navigationBars.top) },
@@ -127,6 +129,8 @@ private class AppProcess(
127
129
  // the night mode last reported to the activity: ActivityRecord compares
128
130
  // the configuration with it before a relaunch or onConfigurationChanged
129
131
  var reportedNight = false
132
+ // the software keyboard's height above the screen's bottom edge, in pixels
133
+ var ime by mutableIntStateOf(0)
130
134
  }
131
135
 
132
136
  private var process: AppProcess? = null
@@ -228,7 +232,7 @@ private fun launchActivity(app: AppProcess, lastNonConfigurationInstance: Any?):
228
232
  app.density, app.fontScale, app.locale, app.gpu, app.stencilBits, app.fonts, app.invalidate,
229
233
  night = isNight(activity.resources.configuration),
230
234
  owners = componentActivity?.let(::ActivityOwners),
231
- insets = DeviceInsets(window, app.insets),
235
+ insets = DeviceInsets(window, app.insets) { app.ime },
232
236
  clockRunning = { componentActivity == null || componentActivity.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED) },
233
237
  )
234
238
  mount.setContent {
@@ -322,6 +326,14 @@ private fun reportConfiguration(app: AppProcess): Boolean {
322
326
  private fun isNight(configuration: Configuration) =
323
327
  configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
324
328
 
329
+ // the software keyboard moved: its inset reaches the window's content
330
+ internal fun peachAppIme(bottom: Int) {
331
+ val app = runningApp()
332
+ if (app.ime == bottom) return
333
+ app.ime = bottom
334
+ app.invalidate(true)
335
+ }
336
+
325
337
  // the device's night mode changed. the process's configuration changes in
326
338
  // place and the Application hears it first; a visible activity then takes it.
327
339
  // a stopped activity takes it once visible again, if it still differs.
@@ -9,9 +9,13 @@ import androidx.compose.runtime.setValue
9
9
  import androidx.compose.runtime.snapshots.Snapshot
10
10
  import androidx.compose.ui.LocalSystemTheme
11
11
  import androidx.compose.ui.geometry.Offset
12
+ import androidx.compose.ui.geometry.Rect
12
13
  import androidx.compose.ui.graphics.asComposeCanvas
13
14
  import androidx.compose.ui.input.InputMode
14
15
  import androidx.compose.ui.input.InputModeManager
16
+ import androidx.compose.ui.input.key.Key
17
+ import androidx.compose.ui.input.key.KeyEvent
18
+ import androidx.compose.ui.input.key.KeyEventType
15
19
  import androidx.compose.ui.input.pointer.PointerEventType
16
20
  import androidx.compose.ui.input.pointer.PointerType
17
21
  import androidx.compose.ui.platform.FrameRecomposer
@@ -19,6 +23,7 @@ import androidx.compose.ui.platform.LocalFontFamilyResolver
19
23
  import androidx.compose.ui.platform.PlatformArchitectureComponentsOwner
20
24
  import androidx.compose.ui.platform.PlatformWindowInsets
21
25
  import androidx.compose.ui.platform.PlatformContext
26
+ import androidx.compose.ui.platform.PlatformTextInputMethodRequest
22
27
  import androidx.compose.ui.platform.WindowInfo
23
28
  import androidx.compose.ui.platform.registerSkikoComposeImplementation
24
29
  import androidx.compose.ui.scene.CanvasLayersComposeScene
@@ -34,6 +39,19 @@ import androidx.compose.ui.text.font.FontWeight
34
39
  import androidx.compose.ui.text.font.PlatformTypefacesLoader
35
40
  import androidx.compose.ui.text.font.createPlatformFontFamilyResolver
36
41
  import androidx.compose.ui.text.font.platformTypefacesLoader
42
+ import androidx.compose.ui.text.input.BackspaceCommand
43
+ import androidx.compose.ui.text.input.CommitTextCommand
44
+ import androidx.compose.ui.text.input.EditCommand
45
+ import androidx.compose.ui.text.input.FinishComposingTextCommand
46
+ import androidx.compose.ui.text.input.ImeAction
47
+ import androidx.compose.ui.text.input.ImeOptions
48
+ import androidx.compose.ui.text.input.KeyboardCapitalization
49
+ import androidx.compose.ui.text.input.KeyboardType
50
+ import androidx.compose.ui.text.input.PlatformTextInputService
51
+ import androidx.compose.ui.text.input.SetComposingRegionCommand
52
+ import androidx.compose.ui.text.input.SetComposingTextCommand
53
+ import androidx.compose.ui.text.input.SetSelectionCommand
54
+ import androidx.compose.ui.text.input.TextFieldValue
37
55
  import androidx.compose.ui.text.intl.LocaleList
38
56
  import androidx.compose.ui.text.intl.setPlatformLocaleProvider
39
57
  import androidx.compose.ui.text.platform.Font
@@ -45,6 +63,7 @@ import androidx.compose.ui.unit.LayoutDirection
45
63
  import dev.onejs.one.OneNativeSourceViewDispatch
46
64
  import kotlin.io.encoding.Base64
47
65
  import kotlinx.coroutines.Dispatchers
66
+ import kotlinx.coroutines.awaitCancellation
48
67
  import org.jetbrains.skiko.SystemTheme
49
68
  import org.jetbrains.skia.BackendRenderTarget
50
69
  import org.jetbrains.skia.Bitmap
@@ -94,6 +113,129 @@ internal class PeachComposePlatform(
94
113
  override fun onSemanticsOwnerRemoved(semanticsOwner: SemanticsOwner) { owners.remove(semanticsOwner) }
95
114
  override fun onSemanticsChange(semanticsOwner: SemanticsOwner) = Unit
96
115
  override fun onLayoutChange(semanticsOwner: SemanticsOwner, semanticsNodeId: Int) = Unit
116
+
117
+ // the input method session the focused text field holds, android's
118
+ // InputConnection. each session gets a new token, so the host can tell a
119
+ // late close from the session that replaced it
120
+ var input: PeachInputSession? = null
121
+ private set
122
+ var onInputChange: () -> Unit = {}
123
+ private var nextInput = 1
124
+
125
+ override suspend fun startInputMethod(request: PlatformTextInputMethodRequest): Nothing {
126
+ val session = PeachInputSession(nextInput++, request)
127
+ input = session
128
+ onInputChange()
129
+ try { awaitCancellation() }
130
+ finally {
131
+ if (input === session) input = null
132
+ onInputChange()
133
+ }
134
+ }
135
+
136
+ // compose's keyboard controller: show and hide move the keyboard and keep
137
+ // the session, as Done does on android. a session opens with the keyboard
138
+ // up, as android's legacy text input service shows it on startInput
139
+ @Suppress("DEPRECATION")
140
+ override val textInputService: PlatformTextInputService = object : PlatformTextInputService {
141
+ override fun startInput(value: TextFieldValue, imeOptions: ImeOptions,
142
+ onEditCommand: (List<EditCommand>) -> Unit, onImeActionPerformed: (ImeAction) -> Unit) = Unit
143
+ override fun stopInput() = Unit
144
+ override fun showSoftwareKeyboard() { input?.request(true); onInputChange() }
145
+ override fun hideSoftwareKeyboard() { input?.request(false); onInputChange() }
146
+ override fun updateState(oldValue: TextFieldValue?, newValue: TextFieldValue) = Unit
147
+ }
148
+ }
149
+
150
+ internal class PeachInputSession(val token: Int, val request: PlatformTextInputMethodRequest) {
151
+ var keyboard = true
152
+ private set
153
+ // each show or hide compose asks for, so a repeat still reaches the keyboard
154
+ private var keyboardRequest = 0
155
+ private val options get() = request.imeOptions
156
+
157
+ // EditorInfo.android.kt: a single line field's default action is Done; a
158
+ // multi line field's default has no enter action, so enter types a newline
159
+ private val action: ImeAction? get() = when (options.imeAction) {
160
+ ImeAction.Default -> if (options.singleLine) ImeAction.Done else null
161
+ ImeAction.None -> null
162
+ else -> options.imeAction
163
+ }
164
+
165
+ fun request(visible: Boolean) {
166
+ keyboard = visible
167
+ keyboardRequest++
168
+ }
169
+
170
+ private fun rect(rect: Rect?) = rect?.let { JSONArray().put(it.left).put(it.top).put(it.right).put(it.bottom) }
171
+
172
+ fun state(): JSONObject {
173
+ val value = request.value()
174
+ return JSONObject().put("token", token).put("keyboard", keyboard).put("keyboardRequest", keyboardRequest)
175
+ .put("value", value.text).put("selectionStart", value.selection.start).put("selectionEnd", value.selection.end)
176
+ .put("singleLine", options.singleLine).put("enterInsertsNewline", action == null && !options.singleLine)
177
+ .put("keyboardType", when (options.keyboardType) {
178
+ KeyboardType.Number -> "number"
179
+ KeyboardType.Phone -> "phone"
180
+ KeyboardType.Uri -> "uri"
181
+ KeyboardType.Email -> "email"
182
+ KeyboardType.Password -> "password"
183
+ KeyboardType.NumberPassword -> "number-password"
184
+ KeyboardType.Decimal -> "decimal"
185
+ KeyboardType.Ascii -> "ascii"
186
+ else -> "text"
187
+ })
188
+ .put("imeAction", when (action) {
189
+ ImeAction.Done -> "done"
190
+ ImeAction.Go -> "go"
191
+ ImeAction.Search -> "search"
192
+ ImeAction.Send -> "send"
193
+ ImeAction.Next -> "next"
194
+ ImeAction.Previous -> "previous"
195
+ else -> "default"
196
+ })
197
+ .put("capitalization", when (options.capitalization) {
198
+ KeyboardCapitalization.Characters -> "characters"
199
+ KeyboardCapitalization.Words -> "words"
200
+ KeyboardCapitalization.Sentences -> "sentences"
201
+ else -> "none"
202
+ })
203
+ .put("autoCorrect", options.autoCorrect)
204
+ .put("field", rect(request.textFieldRectInRoot()))
205
+ .put("caret", rect(request.focusedRectInRoot()))
206
+ }
207
+
208
+ // one keyboard edit, applied as one batch the way RecordingInputConnection
209
+ // ends a batch edit. typed text commits as an IME's commitText does; a
210
+ // composition replaces its own region, else the edit's range
211
+ fun edit(command: JSONObject): Boolean {
212
+ val value = request.value()
213
+ val start = command.optInt("start")
214
+ val end = command.optInt("end")
215
+ val composing = value.composition?.let { it.start == start && it.end == end } == true
216
+ val commands: List<EditCommand> = when (command.getString("kind")) {
217
+ "text" -> listOf(CommitTextCommand(command.getString("text"), 1))
218
+ "backspace" -> listOf(BackspaceCommand())
219
+ "enter" -> {
220
+ val performed = action
221
+ if (performed != null) {
222
+ request.onImeAction?.invoke(performed)
223
+ return true
224
+ }
225
+ if (options.singleLine) return false
226
+ listOf(CommitTextCommand("\n", 1))
227
+ }
228
+ "compositionStart" -> if (start == end) return true else listOf(SetComposingRegionCommand(start, end))
229
+ "replace" -> {
230
+ val text = command.getString("text")
231
+ val place = if (composing) emptyList() else listOf(FinishComposingTextCommand(), SetSelectionCommand(start, end))
232
+ place + if (command.getString("composition") == "update") SetComposingTextCommand(text, 1) else CommitTextCommand(text, 1)
233
+ }
234
+ else -> error("unknown input edit ${command.getString("kind")}")
235
+ }
236
+ request.onEditCommand(commands)
237
+ return true
238
+ }
97
239
  }
98
240
 
99
241
  // android's default and sans-serif families use the same device font bytes as
@@ -150,12 +292,16 @@ internal class PeachComposeMount(
150
292
  recomposer, platform.density, LayoutDirection.Ltr, null, platform,
151
293
  invalidateLayout = { needsMeasure = true; invalidate(true) }, invalidateDraw = { invalidate(false) },
152
294
  )
295
+ // the session's state as last reported; a change reports it again
296
+ private var reportedInput: String? = null
153
297
  private var surface: Surface? = null
154
298
  private var context: DirectContext? = null
155
299
  private var target: BackendRenderTarget? = null
156
300
  private var pixels: Bitmap? = null
157
301
  private var size = IntSize.Zero
158
302
 
303
+ init { platform.onInputChange = { invalidate(false) } }
304
+
159
305
  fun setContent(content: @Composable () -> Unit) {
160
306
  try {
161
307
  scene.setContent {
@@ -221,6 +367,11 @@ internal class PeachComposeMount(
221
367
  for ((name, args) in frameState) events.add(JSONObject().put("name", name).put("args", args()))
222
368
  frameState.clear()
223
369
  reply.put("events", JSONArray(events))
370
+ val input = platform.input?.state()?.toString()
371
+ if (input != reportedInput) {
372
+ reportedInput = input
373
+ reply.put("input", input?.let { JSONObject(it) } ?: JSONObject.NULL)
374
+ }
224
375
  events.clear()
225
376
  if (gpu) checkNotNull(surface).flushAndSubmit()
226
377
  else {
@@ -245,6 +396,37 @@ internal class PeachComposeMount(
245
396
  invalidate(false)
246
397
  }
247
398
 
399
+ private fun session(token: Int) = platform.input?.takeIf { it.token == token }
400
+
401
+ fun inputEdit(token: Int, command: String): Boolean {
402
+ val session = session(token) ?: return false
403
+ return session.edit(JSONObject(command)).also { invalidate(false) }
404
+ }
405
+
406
+ // a hardware key, pressed and released, through compose's key input as an
407
+ // android view dispatches it; true when compose consumed the press
408
+ fun inputKey(token: Int, name: String, ctrl: Boolean, shift: Boolean, alt: Boolean): Boolean {
409
+ if (session(token) == null) return false
410
+ val key = hardwareKeys[name] ?: name.singleOrNull()?.lowercaseChar()?.let { letterKeys["$it"] } ?: return false
411
+ val codePoint = name.singleOrNull()?.code ?: 0
412
+ fun send(type: KeyEventType) = scene.sendKeyEvent(KeyEvent(key, type, codePoint,
413
+ isCtrlPressed = ctrl, isAltPressed = alt, isShiftPressed = shift))
414
+ val consumed = send(KeyEventType.KeyDown)
415
+ send(KeyEventType.KeyUp)
416
+ invalidate(false)
417
+ return consumed
418
+ }
419
+
420
+ fun inputState(token: Int): String = session(token)?.state()?.toString() ?: ""
421
+
422
+ // the host gave focus to another input: the view loses focus, as
423
+ // View.clearFocus does, and compose closes the session itself
424
+ fun inputBlur(token: Int) {
425
+ if (session(token) == null) return
426
+ scene.focusManager.releaseFocus()
427
+ invalidate(false)
428
+ }
429
+
248
430
  fun semantics(): String {
249
431
  val nodes = JSONArray()
250
432
  fun collect(node: SemanticsNode) {
@@ -274,6 +456,19 @@ internal class PeachComposeMount(
274
456
  }
275
457
  }
276
458
 
459
+ // the keys a hardware keyboard sends, by their DOM names
460
+ private val hardwareKeys = mapOf(
461
+ "ArrowLeft" to Key.DirectionLeft, "ArrowRight" to Key.DirectionRight,
462
+ "ArrowUp" to Key.DirectionUp, "ArrowDown" to Key.DirectionDown,
463
+ "Home" to Key.MoveHome, "End" to Key.MoveEnd, "PageUp" to Key.PageUp, "PageDown" to Key.PageDown,
464
+ "Tab" to Key.Tab, "Delete" to Key.Delete, "Backspace" to Key.Backspace,
465
+ "Enter" to Key.Enter, "Escape" to Key.Escape,
466
+ )
467
+ private val letterKeys = listOf(
468
+ Key.A, Key.B, Key.C, Key.D, Key.E, Key.F, Key.G, Key.H, Key.I, Key.J, Key.K, Key.L, Key.M,
469
+ Key.N, Key.O, Key.P, Key.Q, Key.R, Key.S, Key.T, Key.U, Key.V, Key.W, Key.X, Key.Y, Key.Z,
470
+ ).mapIndexed { index, key -> "${'a' + index}" to key }.toMap()
471
+
277
472
  internal val mounts = mutableMapOf<Int, PeachComposeMount>()
278
473
  private var nextMount = 1
279
474
  private var deviceLocale: String? = null
@@ -330,6 +525,11 @@ fun peachViewMount(source: String, view: String, hash: String, props: String,
330
525
  }
331
526
  @JsExport fun peachViewFrame(id: Int, millis: Double): String = checkNotNull(mounts[id]).frame((millis * 1_000_000).toLong())
332
527
  @JsExport fun peachViewPointer(id: Int, phase: String, x: Float, y: Float, millis: Double) = checkNotNull(mounts[id]).pointer(phase, x, y, millis.toLong())
528
+ @JsExport fun peachViewInputEdit(id: Int, token: Int, command: String): Boolean = checkNotNull(mounts[id]).inputEdit(token, command)
529
+ @JsExport fun peachViewInputKey(id: Int, token: Int, key: String, ctrl: Boolean, shift: Boolean, alt: Boolean): Boolean =
530
+ checkNotNull(mounts[id]).inputKey(token, key, ctrl, shift, alt)
531
+ @JsExport fun peachViewInputState(id: Int, token: Int): String = checkNotNull(mounts[id]).inputState(token)
532
+ @JsExport fun peachViewInputBlur(id: Int, token: Int) = checkNotNull(mounts[id]).inputBlur(token)
333
533
  @JsExport fun peachViewSemantics(id: Int): String = checkNotNull(mounts[id]).semantics()
334
534
  @JsExport fun peachViewDispose(id: Int) { mounts.remove(id)?.close() }
335
535
 
@@ -207,6 +207,19 @@ class JSONObject() {
207
207
  return this
208
208
  }
209
209
 
210
+ // android's coercions: any value reads as its string; a number or a
211
+ // numeric string reads as an int, anything else as the fallback
212
+ fun getString(name: String): String = when (val value = get(name)) {
213
+ is String -> value
214
+ else -> value.toString()
215
+ }
216
+
217
+ fun optInt(name: String, fallback: Int = 0): Int = when (val value = opt(name)) {
218
+ is Number -> value.toInt()
219
+ is String -> value.toDoubleOrNull()?.toInt() ?: fallback
220
+ else -> fallback
221
+ }
222
+
210
223
  fun optBoolean(name: String): Boolean = when (val value = opt(name)) {
211
224
  is Boolean -> value
212
225
  is String -> value.equals("true", ignoreCase = true)
@@ -0,0 +1,62 @@
1
+ package dev.contrast.kotlinapp
2
+
3
+ import androidx.compose.animation.AnimatedVisibility
4
+ import androidx.compose.animation.animateColorAsState
5
+ import androidx.compose.animation.core.animateDpAsState
6
+ import androidx.compose.foundation.background
7
+ import androidx.compose.foundation.layout.Arrangement
8
+ import androidx.compose.foundation.layout.Box
9
+ import androidx.compose.foundation.layout.Column
10
+ import androidx.compose.foundation.layout.fillMaxSize
11
+ import androidx.compose.foundation.layout.fillMaxWidth
12
+ import androidx.compose.foundation.layout.height
13
+ import androidx.compose.foundation.layout.padding
14
+ import androidx.compose.foundation.layout.safeDrawingPadding
15
+ import androidx.compose.foundation.layout.width
16
+ import androidx.compose.material3.Button
17
+ import androidx.compose.material3.Text
18
+ import androidx.compose.runtime.Composable
19
+ import androidx.compose.runtime.LaunchedEffect
20
+ import androidx.compose.runtime.withFrameNanos
21
+ import androidx.compose.runtime.getValue
22
+ import androidx.compose.runtime.mutableStateOf
23
+ import androidx.compose.runtime.remember
24
+ import androidx.compose.runtime.setValue
25
+ import androidx.compose.ui.Modifier
26
+ import androidx.compose.ui.graphics.Color
27
+ import androidx.compose.ui.unit.dp
28
+ import kotlin.math.roundToInt
29
+
30
+ // a tap flips one state that AnimatedVisibility, animateDpAsState and
31
+ // animateColorAsState each animate on the frame clock; the label reads the
32
+ // animated width, so it settles only when the animation has finished. took
33
+ // is the frame clock time and frame count from the flip to the width's arrival
34
+ @Composable
35
+ fun Anim() {
36
+ var on by remember { mutableStateOf(false) }
37
+ var flips by remember { mutableStateOf(0) }
38
+ var took by remember { mutableStateOf("none") }
39
+ val target = if (on) 240.dp else 80.dp
40
+ val width by animateDpAsState(target, label = "width")
41
+ LaunchedEffect(flips) {
42
+ if (flips == 0) return@LaunchedEffect
43
+ val start = withFrameNanos { it }
44
+ var count = 0
45
+ var end = start
46
+ while (width != target) end = withFrameNanos { count += 1; it }
47
+ took = "${(end - start) / 1_000_000}ms $count frames"
48
+ }
49
+ val color by animateColorAsState(if (on) Color(0xFF3F51B5) else Color(0xFFE91E63), label = "color")
50
+ Column(
51
+ Modifier.fillMaxSize().background(Color(0xFFE8EEF8)).safeDrawingPadding().padding(16.dp),
52
+ verticalArrangement = Arrangement.spacedBy(12.dp),
53
+ ) {
54
+ Text("Anim: $on width ${if (width == target) "${target.value.roundToInt()}" else "moving"}")
55
+ Button(onClick = { on = !on; flips += 1 }) { Text("Animate") }
56
+ Text("Took: $took")
57
+ Box(Modifier.width(width).height(48.dp).background(color))
58
+ AnimatedVisibility(on) {
59
+ Text("Panel", Modifier.fillMaxWidth().background(Color(0xFF009688)).padding(24.dp), color = Color.White)
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,47 @@
1
+ package dev.contrast.kotlinapp
2
+
3
+ import androidx.compose.foundation.background
4
+ import androidx.compose.foundation.layout.Arrangement
5
+ import androidx.compose.foundation.layout.Column
6
+ import androidx.compose.foundation.layout.fillMaxSize
7
+ import androidx.compose.foundation.layout.fillMaxWidth
8
+ import androidx.compose.foundation.layout.padding
9
+ import androidx.compose.foundation.layout.safeDrawingPadding
10
+ import androidx.compose.foundation.text.KeyboardActions
11
+ import androidx.compose.foundation.text.KeyboardOptions
12
+ import androidx.compose.material3.OutlinedTextField
13
+ import androidx.compose.material3.Text
14
+ import androidx.compose.runtime.Composable
15
+ import androidx.compose.runtime.getValue
16
+ import androidx.compose.runtime.mutableStateOf
17
+ import androidx.compose.runtime.remember
18
+ import androidx.compose.runtime.setValue
19
+ import androidx.compose.ui.Modifier
20
+ import androidx.compose.ui.focus.onFocusChanged
21
+ import androidx.compose.ui.graphics.Color
22
+ import androidx.compose.ui.text.input.ImeAction
23
+ import androidx.compose.ui.unit.dp
24
+
25
+ // a single line field a tap focuses, typing fills and Done finishes, keeping
26
+ // its focus as android does
27
+ @Composable
28
+ fun Form() {
29
+ var name by remember { mutableStateOf("") }
30
+ var focused by remember { mutableStateOf(false) }
31
+ var done by remember { mutableStateOf(0) }
32
+ Column(
33
+ Modifier.fillMaxSize().background(Color(0xFFE8EEF8)).safeDrawingPadding().padding(16.dp),
34
+ verticalArrangement = Arrangement.spacedBy(12.dp),
35
+ ) {
36
+ Text("Name: ${name.ifEmpty { "empty" }} focus $focused done $done")
37
+ OutlinedTextField(
38
+ value = name,
39
+ onValueChange = { name = it },
40
+ label = { Text("Name") },
41
+ singleLine = true,
42
+ keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
43
+ keyboardActions = KeyboardActions(onDone = { done += 1; defaultKeyboardAction(ImeAction.Done) }),
44
+ modifier = Modifier.fillMaxWidth().onFocusChanged { focused = it.isFocused },
45
+ )
46
+ }
47
+ }
@@ -0,0 +1,51 @@
1
+ package dev.contrast.kotlinapp
2
+
3
+ import androidx.compose.foundation.background
4
+ import androidx.compose.foundation.layout.Column
5
+ import androidx.compose.foundation.layout.Spacer
6
+ import androidx.compose.foundation.layout.WindowInsets
7
+ import androidx.compose.foundation.layout.fillMaxSize
8
+ import androidx.compose.foundation.layout.fillMaxWidth
9
+ import androidx.compose.foundation.layout.ime
10
+ import androidx.compose.foundation.layout.padding
11
+ import androidx.compose.foundation.layout.safeDrawingPadding
12
+ import androidx.compose.material3.OutlinedTextField
13
+ import androidx.compose.material3.Text
14
+ import androidx.compose.runtime.Composable
15
+ import androidx.compose.runtime.getValue
16
+ import androidx.compose.runtime.mutableStateOf
17
+ import androidx.compose.runtime.remember
18
+ import androidx.compose.runtime.setValue
19
+ import androidx.compose.ui.Modifier
20
+ import androidx.compose.ui.graphics.Color
21
+ import androidx.compose.ui.layout.onSizeChanged
22
+ import androidx.compose.ui.platform.LocalDensity
23
+ import androidx.compose.ui.unit.dp
24
+
25
+ // safe drawing padding holds a bottom bar above the keyboard, which the IME
26
+ // inset moves as the keyboard opens and closes. a window that fits system
27
+ // windows takes no IME inset, and lifted reports whether its content shrank
28
+ @Composable
29
+ fun Ime() {
30
+ var note by remember { mutableStateOf("") }
31
+ var height by remember { mutableStateOf(0) }
32
+ var tallest by remember { mutableStateOf(0) }
33
+ val shown = WindowInsets.ime.getBottom(LocalDensity.current) > 0
34
+ Column(
35
+ Modifier.fillMaxSize()
36
+ .onSizeChanged { height = it.height; tallest = maxOf(tallest, it.height) }
37
+ .background(Color(0xFFE8EEF8))
38
+ .safeDrawingPadding()
39
+ .padding(16.dp),
40
+ ) {
41
+ Text("Ime: ${if (shown) "shown" else "hidden"} lifted ${height < tallest}")
42
+ OutlinedTextField(
43
+ value = note,
44
+ onValueChange = { note = it },
45
+ label = { Text("Note") },
46
+ modifier = Modifier.fillMaxWidth().padding(top = 12.dp),
47
+ )
48
+ Spacer(Modifier.weight(1f))
49
+ Text("Bottom bar", Modifier.fillMaxWidth().background(Color(0xFF3F51B5)).padding(16.dp), color = Color.White)
50
+ }
51
+ }
@@ -0,0 +1,40 @@
1
+ package dev.contrast.kotlinapp
2
+
3
+ import androidx.compose.foundation.background
4
+ import androidx.compose.foundation.layout.Arrangement
5
+ import androidx.compose.foundation.layout.Column
6
+ import androidx.compose.foundation.layout.fillMaxSize
7
+ import androidx.compose.foundation.layout.fillMaxWidth
8
+ import androidx.compose.foundation.layout.padding
9
+ import androidx.compose.foundation.layout.safeDrawingPadding
10
+ import androidx.compose.material3.OutlinedTextField
11
+ import androidx.compose.material3.Text
12
+ import androidx.compose.runtime.Composable
13
+ import androidx.compose.runtime.getValue
14
+ import androidx.compose.runtime.mutableStateOf
15
+ import androidx.compose.runtime.remember
16
+ import androidx.compose.runtime.setValue
17
+ import androidx.compose.ui.Modifier
18
+ import androidx.compose.ui.focus.onFocusChanged
19
+ import androidx.compose.ui.graphics.Color
20
+ import androidx.compose.ui.unit.dp
21
+
22
+ // a multi line field with default keyboard options: enter inserts a newline
23
+ // and delete removes what it typed
24
+ @Composable
25
+ fun Note() {
26
+ var note by remember { mutableStateOf("") }
27
+ var focused by remember { mutableStateOf(false) }
28
+ Column(
29
+ Modifier.fillMaxSize().background(Color(0xFFE8EEF8)).safeDrawingPadding().padding(16.dp),
30
+ verticalArrangement = Arrangement.spacedBy(12.dp),
31
+ ) {
32
+ Text("Note: ${note.ifEmpty { "empty" }.replace('\n', '|')} focus $focused")
33
+ OutlinedTextField(
34
+ value = note,
35
+ onValueChange = { note = it },
36
+ label = { Text("Note") },
37
+ modifier = Modifier.fillMaxWidth().onFocusChanged { focused = it.isFocused },
38
+ )
39
+ }
40
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "peachsim",
3
- "version": "0.1.666",
3
+ "version": "0.1.667",
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",
@@ -401,6 +401,9 @@ export async function kotlinAppArtifact(
401
401
  '@JsExport',
402
402
  'fun peachAppNight(night: Boolean) = dev.peach.app.peachAppNight(night)',
403
403
  '',
404
+ '@JsExport',
405
+ 'fun peachAppIme(bottom: Int) = dev.peach.app.peachAppIme(bottom)',
406
+ '',
404
407
  ].join('\n')
405
408
  const generated: Record<string, string> = {
406
409
  'PeachEntry.kt': entrySource([]),