pythonnative 0.40.0__cp313-cp313-win_amd64.whl

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 (418) hide show
  1. pythonnative/__init__.py +411 -0
  2. pythonnative/_ios_log.py +92 -0
  3. pythonnative/_yoga.cp313-win_amd64.pyd +0 -0
  4. pythonnative/alerts.py +236 -0
  5. pythonnative/animated.py +1687 -0
  6. pythonnative/animation_graph.py +78 -0
  7. pythonnative/appearance.py +124 -0
  8. pythonnative/bootstrap.py +100 -0
  9. pythonnative/bridge/__init__.py +302 -0
  10. pythonnative/bridge/android.py +78 -0
  11. pythonnative/bridge/codec.py +170 -0
  12. pythonnative/bridge/commits.py +114 -0
  13. pythonnative/bridge/fake.py +243 -0
  14. pythonnative/bridge/ios.py +138 -0
  15. pythonnative/bridge/web.py +489 -0
  16. pythonnative/cli/__init__.py +7 -0
  17. pythonnative/cli/pn.py +1214 -0
  18. pythonnative/component.py +271 -0
  19. pythonnative/components/__init__.py +111 -0
  20. pythonnative/components/_base.py +166 -0
  21. pythonnative/components/controls.py +521 -0
  22. pythonnative/components/layout.py +518 -0
  23. pythonnative/components/lists.py +785 -0
  24. pythonnative/components/media.py +204 -0
  25. pythonnative/components/overlays.py +107 -0
  26. pythonnative/components/pressable.py +252 -0
  27. pythonnative/components/structural.py +167 -0
  28. pythonnative/components/text.py +291 -0
  29. pythonnative/devclient.py +824 -0
  30. pythonnative/devserver/__init__.py +34 -0
  31. pythonnative/devserver/server.py +718 -0
  32. pythonnative/devserver/static/animation_graph.js +82 -0
  33. pythonnative/devserver/static/bridge.js +147 -0
  34. pythonnative/devserver/static/colors.js +70 -0
  35. pythonnative/devserver/static/host.js +552 -0
  36. pythonnative/devserver/static/index.html +60 -0
  37. pythonnative/devserver/static/layout.js +85 -0
  38. pythonnative/devserver/static/preview.css +415 -0
  39. pythonnative/devserver/static/renderer.js +1994 -0
  40. pythonnative/devserver/static/shell.js +345 -0
  41. pythonnative/devserver/static/yoga/LICENSE +21 -0
  42. pythonnative/devserver/static/yoga/binaries/yoga-wasm-base64-esm.js +75 -0
  43. pythonnative/devserver/static/yoga/src/generated/YGEnums.d.ts +189 -0
  44. pythonnative/devserver/static/yoga/src/generated/YGEnums.js +211 -0
  45. pythonnative/devserver/static/yoga/src/generated/YGEnums.js.map +1 -0
  46. pythonnative/devserver/static/yoga/src/index.d.ts +12 -0
  47. pythonnative/devserver/static/yoga/src/index.js +16 -0
  48. pythonnative/devserver/static/yoga/src/index.js.map +1 -0
  49. pythonnative/devserver/static/yoga/src/load.d.ts +11 -0
  50. pythonnative/devserver/static/yoga/src/load.js +17 -0
  51. pythonnative/devserver/static/yoga/src/load.js.map +1 -0
  52. pythonnative/devserver/static/yoga/src/wrapAssembly.d.ts +155 -0
  53. pythonnative/devserver/static/yoga/src/wrapAssembly.js +125 -0
  54. pythonnative/devserver/static/yoga/src/wrapAssembly.js.map +1 -0
  55. pythonnative/devserver/watcher.py +267 -0
  56. pythonnative/devserver/ws.py +421 -0
  57. pythonnative/diagnostics.py +241 -0
  58. pythonnative/element.py +159 -0
  59. pythonnative/equality.py +19 -0
  60. pythonnative/events.py +231 -0
  61. pythonnative/gestures.py +1333 -0
  62. pythonnative/hooks.py +1621 -0
  63. pythonnative/hosts/__init__.py +63 -0
  64. pythonnative/hosts/base.py +596 -0
  65. pythonnative/hosts/native.py +302 -0
  66. pythonnative/hot_reload.py +585 -0
  67. pythonnative/layout.py +328 -0
  68. pythonnative/mutations.py +142 -0
  69. pythonnative/native/android/build.gradle +52 -0
  70. pythonnative/native/android/gradle.properties +3 -0
  71. pythonnative/native/android/settings.gradle +12 -0
  72. pythonnative/native/android/src/main/AndroidManifest.xml +11 -0
  73. pythonnative/native/android/src/main/cpp/CMakeLists.txt +9 -0
  74. pythonnative/native/android/src/main/cpp/yoga_jni.cpp +52 -0
  75. pythonnative/native/android/src/main/java/com/pythonnative/generated/NativeModules.kt +5 -0
  76. pythonnative/native/android/src/main/java/com/pythonnative/generated/NativeProps.kt +3705 -0
  77. pythonnative/native/android/src/main/java/com/pythonnative/generated/PNContracts.kt +129 -0
  78. pythonnative/native/android/src/main/java/com/pythonnative/runtime/PNBridge.kt +261 -0
  79. pythonnative/native/android/src/main/java/com/pythonnative/runtime/PythonHost.kt +20 -0
  80. pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/AnimationGraph.kt +108 -0
  81. pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/AnimationSpecs.kt +141 -0
  82. pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/PNAnimator.kt +257 -0
  83. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/CommitState.kt +106 -0
  84. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/JsonUtil.kt +175 -0
  85. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/MainThread.kt +40 -0
  86. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNLog.kt +33 -0
  87. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNRegistry.kt +82 -0
  88. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNTransaction.kt +80 -0
  89. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/TransactionApplier.kt +117 -0
  90. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/ViewRegistry.kt +74 -0
  91. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/BuiltinComponents.kt +37 -0
  92. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ButtonManager.kt +32 -0
  93. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ComponentManager.kt +213 -0
  94. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ContainerManagers.kt +69 -0
  95. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ControlManagers.kt +186 -0
  96. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/DatePickerManager.kt +123 -0
  97. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ImageLoader.kt +141 -0
  98. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ImageManager.kt +162 -0
  99. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ModalManager.kt +134 -0
  100. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PNColor.kt +201 -0
  101. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PickerManager.kt +86 -0
  102. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PortalManager.kt +55 -0
  103. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PressableManager.kt +122 -0
  104. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ScreenStackManager.kt +110 -0
  105. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ScrollViewManager.kt +214 -0
  106. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/SegmentedControlManager.kt +113 -0
  107. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/StatusBarManager.kt +52 -0
  108. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TabBarManager.kt +131 -0
  109. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TextInputManager.kt +250 -0
  110. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TextManager.kt +233 -0
  111. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ViewStyler.kt +355 -0
  112. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/VirtualListManager.kt +159 -0
  113. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/WebViewManager.kt +149 -0
  114. pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureArbiter.kt +233 -0
  115. pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureCoordinator.kt +127 -0
  116. pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureRecognizers.kt +527 -0
  117. pythonnative/native/android/src/main/java/com/pythonnative/runtime/layout/NativeLayout.kt +145 -0
  118. pythonnative/native/android/src/main/java/com/pythonnative/runtime/layout/YogaNode.kt +29 -0
  119. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/BuiltinModules.kt +73 -0
  120. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/DeviceModules.kt +316 -0
  121. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/HostModule.kt +61 -0
  122. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/MediaModules.kt +200 -0
  123. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/NativeModule.kt +158 -0
  124. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/NotificationsModule.kt +100 -0
  125. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/PermissionsModule.kt +99 -0
  126. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/StorageModules.kt +89 -0
  127. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/SystemModules.kt +178 -0
  128. pythonnative/native/android/src/main/java/com/pythonnative/runtime/plugins/GeneratedPlugins.kt +16 -0
  129. pythonnative/native/android/src/main/java/com/pythonnative/runtime/screens/PNScreenFragment.kt +188 -0
  130. pythonnative/native/android/src/main/java/com/pythonnative/runtime/screens/ScreenRegistry.kt +80 -0
  131. pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNAccessibilityDelegate.kt +48 -0
  132. pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNBorderDrawable.kt +76 -0
  133. pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNEditText.kt +18 -0
  134. pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNFrameLayout.kt +49 -0
  135. pythonnative/native/android/src/test/java/com/pythonnative/runtime/AnimationSpecsTest.kt +75 -0
  136. pythonnative/native/android/src/test/java/com/pythonnative/runtime/GestureArbiterTest.kt +197 -0
  137. pythonnative/native/android/src/test/java/com/pythonnative/runtime/PNColorTest.kt +47 -0
  138. pythonnative/native/android/src/test/java/com/pythonnative/runtime/PNTransactionTest.kt +78 -0
  139. pythonnative/native/android/src/test/java/com/pythonnative/runtime/PromiseTest.kt +89 -0
  140. pythonnative/native/ios/Package.swift +23 -0
  141. pythonnative/native/ios/Sources/PythonNativeKit/Animation/PNAnimationGraph.swift +199 -0
  142. pythonnative/native/ios/Sources/PythonNativeKit/Animation/PNAnimator.swift +312 -0
  143. pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNBridge.swift +255 -0
  144. pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNCommit.swift +104 -0
  145. pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNRegistry.swift +142 -0
  146. pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNTransaction.swift +166 -0
  147. pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNViewRegistry.swift +53 -0
  148. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNButtonManagers.swift +207 -0
  149. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNColor.swift +173 -0
  150. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNComponentManager.swift +183 -0
  151. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNContainerView.swift +56 -0
  152. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNControlManagers.swift +281 -0
  153. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNImageManager.swift +222 -0
  154. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNOverlayManagers.swift +243 -0
  155. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNPressableManager.swift +114 -0
  156. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNScreenStackManager.swift +107 -0
  157. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNScrollViewManager.swift +197 -0
  158. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTabBarManager.swift +106 -0
  159. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTextInputManager.swift +297 -0
  160. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTextManager.swift +230 -0
  161. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTransform.swift +82 -0
  162. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewManager.swift +86 -0
  163. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewState.swift +87 -0
  164. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewStyler.swift +340 -0
  165. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNVirtualListManager.swift +155 -0
  166. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNWebViewManager.swift +124 -0
  167. pythonnative/native/ios/Sources/PythonNativeKit/Generated/NativeModules.swift +1 -0
  168. pythonnative/native/ios/Sources/PythonNativeKit/Generated/NativeProps.swift +3775 -0
  169. pythonnative/native/ios/Sources/PythonNativeKit/Generated/PNContracts.swift +46 -0
  170. pythonnative/native/ios/Sources/PythonNativeKit/Gestures/PNGestureCoordinator.swift +260 -0
  171. pythonnative/native/ios/Sources/PythonNativeKit/Layout/PNLayout.swift +173 -0
  172. pythonnative/native/ios/Sources/PythonNativeKit/Modules/AlertModule.swift +92 -0
  173. pythonnative/native/ios/Sources/PythonNativeKit/Modules/HostModule.swift +80 -0
  174. pythonnative/native/ios/Sources/PythonNativeKit/Modules/LifecycleModules.swift +174 -0
  175. pythonnative/native/ios/Sources/PythonNativeKit/Modules/MediaModules.swift +177 -0
  176. pythonnative/native/ios/Sources/PythonNativeKit/Modules/PNNativeModule.swift +168 -0
  177. pythonnative/native/ios/Sources/PythonNativeKit/Modules/PermissionModules.swift +266 -0
  178. pythonnative/native/ios/Sources/PythonNativeKit/Modules/SystemModules.swift +251 -0
  179. pythonnative/native/ios/Sources/PythonNativeKit/Plugins/PNPluginRegistration.swift +8 -0
  180. pythonnative/native/ios/Sources/PythonNativeKit/Screens/PNScreenRegistry.swift +46 -0
  181. pythonnative/native/ios/Sources/PythonNativeKit/Screens/PNViewController.swift +237 -0
  182. pythonnative/native/ios/Sources/PythonNativeKit/Support/PNCompat.swift +46 -0
  183. pythonnative/native/ios/Sources/PythonNativeKit/Support/PNJSON.swift +173 -0
  184. pythonnative/native/ios/Sources/PythonNativeKit/Support/PNLog.swift +47 -0
  185. pythonnative/native/ios/Sources/PythonNativeKit/Support/PNWindow.swift +51 -0
  186. pythonnative/native/ios/Tests/PythonNativeKitTests/PNAnimationGraphTests.swift +26 -0
  187. pythonnative/native/ios/Tests/PythonNativeKitTests/PNColorTests.swift +50 -0
  188. pythonnative/native/ios/Tests/PythonNativeKitTests/PNCommitTests.swift +86 -0
  189. pythonnative/native/ios/Tests/PythonNativeKitTests/PNGestureTests.swift +101 -0
  190. pythonnative/native/ios/Tests/PythonNativeKitTests/PNManagerTests.swift +126 -0
  191. pythonnative/native/ios/Tests/PythonNativeKitTests/PNModuleTests.swift +111 -0
  192. pythonnative/native/ios/Tests/PythonNativeKitTests/PNTransactionTests.swift +91 -0
  193. pythonnative/native/yoga/LICENSE +21 -0
  194. pythonnative/native/yoga/Package.swift +9 -0
  195. pythonnative/native/yoga/VENDORED.md +2 -0
  196. pythonnative/native/yoga/include/PNStyle.h +10 -0
  197. pythonnative/native/yoga/include/module.modulemap +5 -0
  198. pythonnative/native/yoga/include/yoga/YGConfig.h +158 -0
  199. pythonnative/native/yoga/include/yoga/YGEnums.h +142 -0
  200. pythonnative/native/yoga/include/yoga/YGMacros.h +99 -0
  201. pythonnative/native/yoga/include/yoga/YGNode.h +304 -0
  202. pythonnative/native/yoga/include/yoga/YGNodeLayout.h +35 -0
  203. pythonnative/native/yoga/include/yoga/YGNodeStyle.h +130 -0
  204. pythonnative/native/yoga/include/yoga/YGPixelGrid.h +29 -0
  205. pythonnative/native/yoga/include/yoga/YGValue.h +84 -0
  206. pythonnative/native/yoga/include/yoga/Yoga.h +21 -0
  207. pythonnative/native/yoga/python.cpp +16 -0
  208. pythonnative/native/yoga/style.cpp +272 -0
  209. pythonnative/native/yoga/yoga/CMakeLists.txt +41 -0
  210. pythonnative/native/yoga/yoga/YGConfig.cpp +92 -0
  211. pythonnative/native/yoga/yoga/YGConfig.h +158 -0
  212. pythonnative/native/yoga/yoga/YGEnums.cpp +262 -0
  213. pythonnative/native/yoga/yoga/YGEnums.h +142 -0
  214. pythonnative/native/yoga/yoga/YGMacros.h +99 -0
  215. pythonnative/native/yoga/yoga/YGNode.cpp +366 -0
  216. pythonnative/native/yoga/yoga/YGNode.h +304 -0
  217. pythonnative/native/yoga/yoga/YGNodeLayout.cpp +92 -0
  218. pythonnative/native/yoga/yoga/YGNodeLayout.h +35 -0
  219. pythonnative/native/yoga/yoga/YGNodeStyle.cpp +405 -0
  220. pythonnative/native/yoga/yoga/YGNodeStyle.h +130 -0
  221. pythonnative/native/yoga/yoga/YGPixelGrid.cpp +22 -0
  222. pythonnative/native/yoga/yoga/YGPixelGrid.h +29 -0
  223. pythonnative/native/yoga/yoga/YGValue.cpp +20 -0
  224. pythonnative/native/yoga/yoga/YGValue.h +84 -0
  225. pythonnative/native/yoga/yoga/Yoga.h +21 -0
  226. pythonnative/native/yoga/yoga/algorithm/AbsoluteLayout.cpp +563 -0
  227. pythonnative/native/yoga/yoga/algorithm/AbsoluteLayout.h +41 -0
  228. pythonnative/native/yoga/yoga/algorithm/Align.h +72 -0
  229. pythonnative/native/yoga/yoga/algorithm/Baseline.cpp +78 -0
  230. pythonnative/native/yoga/yoga/algorithm/Baseline.h +21 -0
  231. pythonnative/native/yoga/yoga/algorithm/BoundAxis.h +79 -0
  232. pythonnative/native/yoga/yoga/algorithm/Cache.cpp +121 -0
  233. pythonnative/native/yoga/yoga/algorithm/Cache.h +30 -0
  234. pythonnative/native/yoga/yoga/algorithm/CalculateLayout.cpp +2428 -0
  235. pythonnative/native/yoga/yoga/algorithm/CalculateLayout.h +38 -0
  236. pythonnative/native/yoga/yoga/algorithm/FlexDirection.h +120 -0
  237. pythonnative/native/yoga/yoga/algorithm/FlexLine.cpp +124 -0
  238. pythonnative/native/yoga/yoga/algorithm/FlexLine.h +75 -0
  239. pythonnative/native/yoga/yoga/algorithm/PixelGrid.cpp +132 -0
  240. pythonnative/native/yoga/yoga/algorithm/PixelGrid.h +29 -0
  241. pythonnative/native/yoga/yoga/algorithm/SizingMode.h +73 -0
  242. pythonnative/native/yoga/yoga/algorithm/TrailingPosition.h +44 -0
  243. pythonnative/native/yoga/yoga/config/Config.cpp +135 -0
  244. pythonnative/native/yoga/yoga/config/Config.h +92 -0
  245. pythonnative/native/yoga/yoga/debug/AssertFatal.cpp +53 -0
  246. pythonnative/native/yoga/yoga/debug/AssertFatal.h +29 -0
  247. pythonnative/native/yoga/yoga/debug/Log.cpp +107 -0
  248. pythonnative/native/yoga/yoga/debug/Log.h +34 -0
  249. pythonnative/native/yoga/yoga/enums/Align.h +47 -0
  250. pythonnative/native/yoga/yoga/enums/BoxSizing.h +40 -0
  251. pythonnative/native/yoga/yoga/enums/Dimension.h +40 -0
  252. pythonnative/native/yoga/yoga/enums/Direction.h +41 -0
  253. pythonnative/native/yoga/yoga/enums/Display.h +41 -0
  254. pythonnative/native/yoga/yoga/enums/Edge.h +47 -0
  255. pythonnative/native/yoga/yoga/enums/Errata.h +41 -0
  256. pythonnative/native/yoga/yoga/enums/ExperimentalFeature.h +39 -0
  257. pythonnative/native/yoga/yoga/enums/FlexDirection.h +42 -0
  258. pythonnative/native/yoga/yoga/enums/Gutter.h +41 -0
  259. pythonnative/native/yoga/yoga/enums/Justify.h +44 -0
  260. pythonnative/native/yoga/yoga/enums/LogLevel.h +44 -0
  261. pythonnative/native/yoga/yoga/enums/MeasureMode.h +41 -0
  262. pythonnative/native/yoga/yoga/enums/NodeType.h +40 -0
  263. pythonnative/native/yoga/yoga/enums/Overflow.h +41 -0
  264. pythonnative/native/yoga/yoga/enums/PhysicalEdge.h +21 -0
  265. pythonnative/native/yoga/yoga/enums/PositionType.h +41 -0
  266. pythonnative/native/yoga/yoga/enums/Unit.h +42 -0
  267. pythonnative/native/yoga/yoga/enums/Wrap.h +41 -0
  268. pythonnative/native/yoga/yoga/enums/YogaEnums.h +85 -0
  269. pythonnative/native/yoga/yoga/event/event.cpp +87 -0
  270. pythonnative/native/yoga/yoga/event/event.h +130 -0
  271. pythonnative/native/yoga/yoga/module.modulemap +21 -0
  272. pythonnative/native/yoga/yoga/node/CachedMeasurement.h +53 -0
  273. pythonnative/native/yoga/yoga/node/LayoutResults.cpp +48 -0
  274. pythonnative/native/yoga/yoga/node/LayoutResults.h +122 -0
  275. pythonnative/native/yoga/yoga/node/LayoutableChildren.h +148 -0
  276. pythonnative/native/yoga/yoga/node/Node.cpp +443 -0
  277. pythonnative/native/yoga/yoga/node/Node.h +337 -0
  278. pythonnative/native/yoga/yoga/numeric/Comparison.h +81 -0
  279. pythonnative/native/yoga/yoga/numeric/FloatOptional.h +93 -0
  280. pythonnative/native/yoga/yoga/style/SmallValueBuffer.h +133 -0
  281. pythonnative/native/yoga/yoga/style/Style.h +757 -0
  282. pythonnative/native/yoga/yoga/style/StyleLength.h +107 -0
  283. pythonnative/native/yoga/yoga/style/StyleValueHandle.h +98 -0
  284. pythonnative/native/yoga/yoga/style/StyleValuePool.h +127 -0
  285. pythonnative/native_modules/__init__.py +124 -0
  286. pythonnative/native_modules/app_state.py +99 -0
  287. pythonnative/native_modules/battery.py +75 -0
  288. pythonnative/native_modules/biometrics.py +42 -0
  289. pythonnative/native_modules/camera.py +65 -0
  290. pythonnative/native_modules/clipboard.py +46 -0
  291. pythonnative/native_modules/fallback.py +324 -0
  292. pythonnative/native_modules/file_system.py +145 -0
  293. pythonnative/native_modules/haptics.py +75 -0
  294. pythonnative/native_modules/linking.py +123 -0
  295. pythonnative/native_modules/location.py +75 -0
  296. pythonnative/native_modules/net_info.py +112 -0
  297. pythonnative/native_modules/notifications.py +129 -0
  298. pythonnative/native_modules/permissions.py +108 -0
  299. pythonnative/native_modules/registry.py +526 -0
  300. pythonnative/native_modules/secure_store.py +52 -0
  301. pythonnative/native_modules/share.py +51 -0
  302. pythonnative/native_views/__init__.py +375 -0
  303. pythonnative/native_views/base.py +316 -0
  304. pythonnative/native_views/bridge_backend.py +277 -0
  305. pythonnative/navigation/__init__.py +103 -0
  306. pythonnative/navigation/container.py +154 -0
  307. pythonnative/navigation/handle.py +540 -0
  308. pythonnative/navigation/hooks.py +133 -0
  309. pythonnative/navigation/host.py +58 -0
  310. pythonnative/navigation/linking.py +200 -0
  311. pythonnative/navigation/navigators.py +659 -0
  312. pythonnative/navigation/screen.py +149 -0
  313. pythonnative/navigation/state.py +269 -0
  314. pythonnative/net.py +248 -0
  315. pythonnative/platform.py +166 -0
  316. pythonnative/platform_metrics.py +252 -0
  317. pythonnative/preview.py +287 -0
  318. pythonnative/profiling.py +101 -0
  319. pythonnative/project/__init__.py +68 -0
  320. pythonnative/project/android.py +557 -0
  321. pythonnative/project/builder.py +817 -0
  322. pythonnative/project/config.py +773 -0
  323. pythonnative/project/deps.py +719 -0
  324. pythonnative/project/devices.py +304 -0
  325. pythonnative/project/doctor.py +283 -0
  326. pythonnative/project/fingerprint.py +171 -0
  327. pythonnative/project/icons.py +247 -0
  328. pythonnative/project/ios.py +325 -0
  329. pythonnative/project/lockfile.py +100 -0
  330. pythonnative/project/permissions.py +361 -0
  331. pythonnative/project/plugins.py +519 -0
  332. pythonnative/project/runtime_assets.py +183 -0
  333. pythonnative/py.typed +0 -0
  334. pythonnative/query.py +153 -0
  335. pythonnative/reconciler/__init__.py +29 -0
  336. pythonnative/reconciler/boundaries.py +375 -0
  337. pythonnative/reconciler/children.py +88 -0
  338. pythonnative/reconciler/core.py +1165 -0
  339. pythonnative/reconciler/layout_pass.py +393 -0
  340. pythonnative/reconciler/vnode.py +266 -0
  341. pythonnative/refresh.py +55 -0
  342. pythonnative/runtime.py +306 -0
  343. pythonnative/scheduler.py +159 -0
  344. pythonnative/sdk/__init__.py +153 -0
  345. pythonnative/sdk/_components.py +427 -0
  346. pythonnative/sdk/builtins.py +91 -0
  347. pythonnative/sdk/codegen.py +149 -0
  348. pythonnative/sdk/module_codegen.py +184 -0
  349. pythonnative/sdk/schema.py +224 -0
  350. pythonnative/sdk/templates/contracts.kt +46 -0
  351. pythonnative/sdk/templates/contracts.swift +46 -0
  352. pythonnative/storage.py +184 -0
  353. pythonnative/style.py +841 -0
  354. pythonnative/suspense.py +183 -0
  355. pythonnative/templates/android_template/app/build.gradle +75 -0
  356. pythonnative/templates/android_template/app/proguard-rules.pro +21 -0
  357. pythonnative/templates/android_template/app/src/androidTest/java/com/pythonnative/android_template/ExampleInstrumentedTest.kt +24 -0
  358. pythonnative/templates/android_template/app/src/debug/AndroidManifest.xml +7 -0
  359. pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +32 -0
  360. pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +130 -0
  361. pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +13 -0
  362. pythonnative/templates/android_template/app/src/main/res/drawable/ic_launcher_background.xml +170 -0
  363. pythonnative/templates/android_template/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +30 -0
  364. pythonnative/templates/android_template/app/src/main/res/layout/activity_main.xml +10 -0
  365. pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +6 -0
  366. pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +6 -0
  367. pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
  368. pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
  369. pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
  370. pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
  371. pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
  372. pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
  373. pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
  374. pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
  375. pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
  376. pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
  377. pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml +22 -0
  378. pythonnative/templates/android_template/app/src/main/res/values/colors.xml +5 -0
  379. pythonnative/templates/android_template/app/src/main/res/values/strings.xml +5 -0
  380. pythonnative/templates/android_template/app/src/main/res/values/themes.xml +9 -0
  381. pythonnative/templates/android_template/app/src/main/res/values-night/themes.xml +7 -0
  382. pythonnative/templates/android_template/app/src/main/res/xml/backup_rules.xml +13 -0
  383. pythonnative/templates/android_template/app/src/main/res/xml/data_extraction_rules.xml +19 -0
  384. pythonnative/templates/android_template/app/src/test/java/com/pythonnative/android_template/ExampleUnitTest.kt +17 -0
  385. pythonnative/templates/android_template/build.gradle +10 -0
  386. pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.jar +0 -0
  387. pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +6 -0
  388. pythonnative/templates/android_template/gradle.properties +23 -0
  389. pythonnative/templates/android_template/gradlew +185 -0
  390. pythonnative/templates/android_template/gradlew.bat +89 -0
  391. pythonnative/templates/android_template/settings.gradle +17 -0
  392. pythonnative/templates/ios_template/ios_template/AppDelegate.swift +51 -0
  393. pythonnative/templates/ios_template/ios_template/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
  394. pythonnative/templates/ios_template/ios_template/Assets.xcassets/AppIcon.appiconset/Contents.json +13 -0
  395. pythonnative/templates/ios_template/ios_template/Assets.xcassets/Contents.json +6 -0
  396. pythonnative/templates/ios_template/ios_template/Base.lproj/LaunchScreen.storyboard +25 -0
  397. pythonnative/templates/ios_template/ios_template/BridgingHeader.h +14 -0
  398. pythonnative/templates/ios_template/ios_template/Info.plist +28 -0
  399. pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +390 -0
  400. pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +61 -0
  401. pythonnative/templates/ios_template/ios_template/ViewController.swift +23 -0
  402. pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +745 -0
  403. pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  404. pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift +36 -0
  405. pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift +41 -0
  406. pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift +32 -0
  407. pythonnative/testing/__init__.py +53 -0
  408. pythonnative/testing/backend.py +276 -0
  409. pythonnative/testing/harness.py +369 -0
  410. pythonnative/utils.py +145 -0
  411. pythonnative-0.40.0.dist-info/DELVEWHEEL +2 -0
  412. pythonnative-0.40.0.dist-info/METADATA +150 -0
  413. pythonnative-0.40.0.dist-info/RECORD +418 -0
  414. pythonnative-0.40.0.dist-info/WHEEL +5 -0
  415. pythonnative-0.40.0.dist-info/entry_points.txt +2 -0
  416. pythonnative-0.40.0.dist-info/licenses/LICENSE +21 -0
  417. pythonnative-0.40.0.dist-info/top_level.txt +1 -0
  418. pythonnative.libs/msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll +0 -0
@@ -0,0 +1,174 @@
1
+ import Network
2
+ import UIKit
3
+
4
+ /// `AppState`: foreground / background transitions.
5
+ ///
6
+ /// The scene delegate calls `AppStateModule.dispatch(_:)`; the module
7
+ /// emits `change` with the state string (`"active"`, `"inactive"`,
8
+ /// `"background"`) as the payload, which is what the Python facade reads.
9
+ public final class AppStateModule: PNNativeModule {
10
+ public static let name = "AppState"
11
+ public private(set) static var current = "active"
12
+
13
+ public init() {}
14
+
15
+ public func call(_ method: String, args: [String: Any], promise: PNPromise) {
16
+ switch method {
17
+ case "current_state":
18
+ promise.resolve(AppStateModule.current)
19
+ default:
20
+ promise.reject("AppState has no method '\(method)'", code: "unknown_method")
21
+ }
22
+ }
23
+
24
+ /// Record and publish a lifecycle transition.
25
+ public static func dispatch(_ state: String) {
26
+ guard ["active", "inactive", "background"].contains(state), state != current else { return }
27
+ current = state
28
+ PNBridge.shared.whenCallbackRegistered {
29
+ PNBridge.shared.callPython(kind: "module", tag: 0, name: name, payload: PNJSON.encode(["event": "change", "payload": state]))
30
+ }
31
+ }
32
+ }
33
+
34
+ /// `Battery`: level / state getters plus `change` events while monitoring.
35
+ public final class BatteryModule: PNNativeModule {
36
+ public static let name = "Battery"
37
+ private static var observers: [NSObjectProtocol] = []
38
+
39
+ public init() {
40
+ BatteryModule.startMonitoring()
41
+ }
42
+
43
+ public func call(_ method: String, args: [String: Any], promise: PNPromise) {
44
+ switch method {
45
+ case "get_level":
46
+ promise.resolve(Double(UIDevice.current.batteryLevel))
47
+ case "get_state":
48
+ promise.resolve(BatteryModule.stateName())
49
+ default:
50
+ promise.reject("Battery has no method '\(method)'", code: "unknown_method")
51
+ }
52
+ }
53
+
54
+ /// Enable battery monitoring and forward UIDevice notifications.
55
+ public static func startMonitoring() {
56
+ guard observers.isEmpty else { return }
57
+ UIDevice.current.isBatteryMonitoringEnabled = true
58
+ let center = NotificationCenter.default
59
+ for notification in [UIDevice.batteryLevelDidChangeNotification, UIDevice.batteryStateDidChangeNotification] {
60
+ observers.append(center.addObserver(forName: notification, object: nil, queue: .main) { _ in
61
+ BatteryModule.dispatch()
62
+ })
63
+ }
64
+ }
65
+
66
+ /// Emit the current `{"level", "state"}` snapshot.
67
+ public static func dispatch() {
68
+ PNModuleEvents.emit(module: name, event: "change", payload: [
69
+ "level": Double(UIDevice.current.batteryLevel),
70
+ "state": stateName(),
71
+ ])
72
+ }
73
+
74
+ static func stateName() -> String {
75
+ switch UIDevice.current.batteryState {
76
+ case .unplugged: return "unplugged"
77
+ case .charging: return "charging"
78
+ case .full: return "full"
79
+ default: return "unknown"
80
+ }
81
+ }
82
+ }
83
+
84
+ /// `NetInfo`: connectivity snapshots from `NWPathMonitor`.
85
+ public final class NetInfoModule: PNNativeModule {
86
+ public static let name = "NetInfo"
87
+
88
+ private let monitor = NWPathMonitor()
89
+ private var last: [String: Any]?
90
+
91
+ public init() {
92
+ monitor.pathUpdateHandler = { [weak self] path in
93
+ let snapshot = NetInfoModule.snapshot(path)
94
+ DispatchQueue.main.async {
95
+ guard let self = self else { return }
96
+ if let last = self.last, NSDictionary(dictionary: last).isEqual(to: snapshot) { return }
97
+ self.last = snapshot
98
+ PNModuleEvents.emit(module: NetInfoModule.name, event: "change", payload: snapshot)
99
+ }
100
+ }
101
+ monitor.start(queue: DispatchQueue(label: "com.pythonnative.netinfo"))
102
+ }
103
+
104
+ deinit {
105
+ monitor.cancel()
106
+ }
107
+
108
+ public func call(_ method: String, args: [String: Any], promise: PNPromise) {
109
+ switch method {
110
+ case "fetch":
111
+ let snapshot = NetInfoModule.snapshot(monitor.currentPath)
112
+ last = snapshot
113
+ promise.resolve(snapshot)
114
+ default:
115
+ promise.reject("NetInfo has no method '\(method)'", code: "unknown_method")
116
+ }
117
+ }
118
+
119
+ static func snapshot(_ path: NWPath) -> [String: Any] {
120
+ let connected = path.status == .satisfied
121
+ let type: String
122
+ if !connected {
123
+ type = "none"
124
+ } else if path.usesInterfaceType(.wifi) {
125
+ type = "wifi"
126
+ } else if path.usesInterfaceType(.cellular) {
127
+ type = "cellular"
128
+ } else if path.usesInterfaceType(.wiredEthernet) {
129
+ type = "ethernet"
130
+ } else {
131
+ type = "unknown"
132
+ }
133
+ return ["is_connected": connected, "type": type, "is_internet_reachable": connected]
134
+ }
135
+ }
136
+
137
+ /// `Linking`: open URLs and deliver inbound deep links.
138
+ ///
139
+ /// `deliver(url:)` buffers until Python's callback is registered so a
140
+ /// cold-start deep link reaches `Linking.get_initial_url()`.
141
+ public final class LinkingModule: PNNativeModule {
142
+ public static let name = "Linking"
143
+
144
+ public init() {}
145
+
146
+ public func call(_ method: String, args: [String: Any], promise: PNPromise) {
147
+ switch method {
148
+ case "open_url":
149
+ guard let url = PNProps.string(args["url"]).flatMap({ URL(string: $0) }) else { return promise.resolve(false) }
150
+ let app = UIApplication.shared
151
+ guard app.canOpenURL(url) || url.scheme?.hasPrefix("http") == true else { return promise.resolve(false) }
152
+ app.open(url, options: [:]) { _ in }
153
+ promise.resolve(true)
154
+ case "can_open_url":
155
+ guard let url = PNProps.string(args["url"]).flatMap({ URL(string: $0) }) else { return promise.resolve(false) }
156
+ promise.resolve(UIApplication.shared.canOpenURL(url))
157
+ case "open_settings":
158
+ guard let url = URL(string: UIApplication.openSettingsURLString) else { return promise.resolve(false) }
159
+ UIApplication.shared.open(url, options: [:]) { _ in }
160
+ promise.resolve(true)
161
+ default:
162
+ promise.reject("Linking has no method '\(method)'", code: "unknown_method")
163
+ }
164
+ }
165
+
166
+ /// Forward an inbound URL as a `url` event (payload: the URL string).
167
+ public static func deliver(url: String) {
168
+ PNBridge.shared.whenCallbackRegistered {
169
+ PNMain.run {
170
+ PNBridge.shared.callPython(kind: "module", tag: 0, name: name, payload: PNJSON.encode(["event": "url", "payload": url]))
171
+ }
172
+ }
173
+ }
174
+ }
@@ -0,0 +1,177 @@
1
+ import CoreLocation
2
+ import UIKit
3
+
4
+ /// `Camera`: `take_photo` / `pick_from_gallery` through `UIImagePickerController`.
5
+ public final class CameraModule: PNNativeModule {
6
+ public static let name = "Camera"
7
+
8
+ private var activePicker: PNImagePickerSession?
9
+
10
+ public init() {}
11
+
12
+ public func call(_ method: String, args: [String: Any], promise: PNPromise) {
13
+ let source: UIImagePickerController.SourceType
14
+ switch method {
15
+ case "take_photo": source = .camera
16
+ case "pick_from_gallery": source = .photoLibrary
17
+ default:
18
+ promise.reject("Camera has no method '\(method)'", code: "unknown_method")
19
+ return
20
+ }
21
+ guard UIImagePickerController.isSourceTypeAvailable(source), let top = PNWindow.topViewController() else {
22
+ promise.resolve(nil)
23
+ return
24
+ }
25
+ if activePicker != nil {
26
+ promise.reject("a picker is already open", code: "busy")
27
+ return
28
+ }
29
+ let quality = PNProps.double(args["quality"]) ?? 0.9
30
+ let session = PNImagePickerSession(quality: CGFloat(max(0, min(1, quality)))) { [weak self] path in
31
+ self?.activePicker = nil
32
+ promise.resolve(path)
33
+ }
34
+ activePicker = session
35
+ let picker = UIImagePickerController()
36
+ picker.sourceType = source
37
+ picker.allowsEditing = PNProps.bool(args["allow_editing"]) ?? false
38
+ picker.delegate = session
39
+ top.present(picker, animated: true)
40
+ }
41
+ }
42
+
43
+ /// Picker delegate that writes the chosen image to Caches and reports its path.
44
+ final class PNImagePickerSession: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
45
+ private let quality: CGFloat
46
+ private let done: (String?) -> Void
47
+
48
+ init(quality: CGFloat, done: @escaping (String?) -> Void) {
49
+ self.quality = quality
50
+ self.done = done
51
+ }
52
+
53
+ func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
54
+ picker.dismiss(animated: true)
55
+ let image = (info[.editedImage] as? UIImage) ?? (info[.originalImage] as? UIImage)
56
+ guard let image = image, let data = image.jpegData(compressionQuality: quality) else {
57
+ done(nil)
58
+ return
59
+ }
60
+ let caches = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first ?? URL(fileURLWithPath: NSTemporaryDirectory())
61
+ let dir = caches.appendingPathComponent("pn_camera", isDirectory: true)
62
+ try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
63
+ let file = dir.appendingPathComponent("\(UUID().uuidString).jpg")
64
+ do {
65
+ try data.write(to: file, options: [.atomic])
66
+ done(file.path)
67
+ } catch {
68
+ PNLog.modules.error("could not save picked image: \(error.localizedDescription)")
69
+ done(nil)
70
+ }
71
+ }
72
+
73
+ func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
74
+ picker.dismiss(animated: true)
75
+ done(nil)
76
+ }
77
+ }
78
+
79
+ /// `Location`: one-shot `get_current` fix via `CLLocationManager`.
80
+ public final class LocationModule: PNNativeModule {
81
+ public static let name = "Location"
82
+
83
+ private var sessions: [PNLocationSession] = []
84
+
85
+ public init() {}
86
+
87
+ public func call(_ method: String, args: [String: Any], promise: PNPromise) {
88
+ guard method == "get_current" else {
89
+ promise.reject("Location has no method '\(method)'", code: "unknown_method")
90
+ return
91
+ }
92
+ let timeout = PNProps.double(args["timeout"]) ?? 15
93
+ var session: PNLocationSession?
94
+ session = PNLocationSession(accuracy: PNProps.string(args["accuracy"]), timeout: timeout) { [weak self] fix in
95
+ if let finished = session {
96
+ self?.sessions.removeAll { $0 === finished }
97
+ }
98
+ promise.resolve(fix)
99
+ }
100
+ guard let started = session else { return }
101
+ sessions.append(started)
102
+ started.start()
103
+ }
104
+ }
105
+
106
+ /// One location request: authorization, a single fix, and a timeout.
107
+ final class PNLocationSession: NSObject, CLLocationManagerDelegate {
108
+ private let manager = CLLocationManager()
109
+ private let done: ([String: Any]?) -> Void
110
+ private let timeout: TimeInterval
111
+ private var finished = false
112
+ private var timer: Timer?
113
+
114
+ init(accuracy: String?, timeout: TimeInterval, done: @escaping ([String: Any]?) -> Void) {
115
+ self.done = done
116
+ self.timeout = max(1, timeout)
117
+ super.init()
118
+ manager.delegate = self
119
+ switch accuracy {
120
+ case "high", "best": manager.desiredAccuracy = kCLLocationAccuracyBest
121
+ case "low", "coarse": manager.desiredAccuracy = kCLLocationAccuracyKilometer
122
+ default: manager.desiredAccuracy = kCLLocationAccuracyHundredMeters
123
+ }
124
+ }
125
+
126
+ func start() {
127
+ timer = Timer.scheduledTimer(withTimeInterval: timeout, repeats: false) { [weak self] _ in
128
+ self?.finish(nil)
129
+ }
130
+ switch manager.pnAuthorizationStatus {
131
+ case .notDetermined:
132
+ manager.requestWhenInUseAuthorization()
133
+ case .denied, .restricted:
134
+ finish(nil)
135
+ default:
136
+ manager.requestLocation()
137
+ }
138
+ }
139
+
140
+ func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
141
+ switch manager.pnAuthorizationStatus {
142
+ case .authorizedAlways, .authorizedWhenInUse:
143
+ manager.requestLocation()
144
+ case .denied, .restricted:
145
+ finish(nil)
146
+ default:
147
+ break
148
+ }
149
+ }
150
+
151
+ func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
152
+ guard let location = locations.last else { return }
153
+ var fix: [String: Any] = [
154
+ "latitude": location.coordinate.latitude,
155
+ "longitude": location.coordinate.longitude,
156
+ "accuracy": location.horizontalAccuracy,
157
+ "altitude": location.altitude,
158
+ "timestamp": location.timestamp.timeIntervalSince1970,
159
+ ]
160
+ if location.speed >= 0 { fix["speed"] = location.speed }
161
+ if location.course >= 0 { fix["heading"] = location.course }
162
+ finish(fix)
163
+ }
164
+
165
+ func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
166
+ PNLog.modules.error("location failed: \(error.localizedDescription)")
167
+ finish(nil)
168
+ }
169
+
170
+ private func finish(_ fix: [String: Any]?) {
171
+ if finished { return }
172
+ finished = true
173
+ timer?.invalidate()
174
+ manager.stopUpdatingLocation()
175
+ done(fix)
176
+ }
177
+ }
@@ -0,0 +1,168 @@
1
+ import Foundation
2
+
3
+ /// A named native module callable from Python through `pn_bridge_call`.
4
+ public protocol PNNativeModule: AnyObject {
5
+ /// The module name Python addresses (`native_module("Clipboard")`).
6
+ static var name: String { get }
7
+ init()
8
+ /// Handle one method call, settling `promise` now or later.
9
+ func call(_ method: String, args: [String: Any], promise: PNPromise)
10
+ }
11
+
12
+ /// The settlement handle for one module call.
13
+ ///
14
+ /// Resolving or rejecting before `call` returns answers Python inline;
15
+ /// settling afterwards posts `callback("module", 0, module,
16
+ /// {"call_id": n, "ok": ..., ...})` on the main thread.
17
+ public final class PNPromise {
18
+ public let callId: Int64
19
+ public let module: String
20
+ public let method: String
21
+
22
+ private(set) var result: [String: Any]?
23
+ private var returned = false
24
+ private let lock = NSLock()
25
+ private var cancellation: (() -> Void)?
26
+ private(set) public var isCancelled = false
27
+ var onFinished: (() -> Void)?
28
+
29
+ public func onCancel(_ callback: @escaping () -> Void) {
30
+ lock.lock()
31
+ let cancelled = isCancelled
32
+ if !cancelled && result == nil { cancellation = callback }
33
+ lock.unlock()
34
+ if cancelled { callback() }
35
+ }
36
+
37
+ func cancel() {
38
+ lock.lock()
39
+ guard result == nil && !isCancelled else { lock.unlock(); return }
40
+ isCancelled = true
41
+ let callback = cancellation
42
+ cancellation = nil
43
+ lock.unlock()
44
+ onFinished?()
45
+ callback?()
46
+ }
47
+
48
+ init(callId: Int64, module: String, method: String) {
49
+ self.callId = callId
50
+ self.module = module
51
+ self.method = method
52
+ }
53
+
54
+ /// Whether the promise has been resolved or rejected.
55
+ public var isSettled: Bool {
56
+ lock.lock()
57
+ defer { lock.unlock() }
58
+ return result != nil || isCancelled
59
+ }
60
+
61
+ /// Settle successfully with `value` (JSON-encodable or `nil`).
62
+ public func resolve(_ value: Any?) {
63
+ settle(["ok": true, "value": value ?? NSNull()])
64
+ }
65
+
66
+ /// Settle with an error message and optional machine-readable code.
67
+ public func reject(_ message: String, code: String? = nil) {
68
+ var envelope: [String: Any] = ["ok": false, "error": message]
69
+ if let code = code { envelope["code"] = code }
70
+ settle(envelope)
71
+ }
72
+
73
+ /// Settle from a Swift `Error`.
74
+ public func reject(_ error: Error) {
75
+ reject(error.localizedDescription, code: (error as NSError).domain)
76
+ }
77
+
78
+ private func settle(_ envelope: [String: Any]) {
79
+ lock.lock()
80
+ if isCancelled { lock.unlock(); return }
81
+ if result != nil {
82
+ lock.unlock()
83
+ PNLog.rateLimited(PNLog.modules, key: "double-settle", "\(module).\(method) settled twice; ignoring")
84
+ return
85
+ }
86
+ result = envelope
87
+ cancellation = nil
88
+ let deliverAsync = returned
89
+ lock.unlock()
90
+ onFinished?()
91
+ if deliverAsync {
92
+ var payload = envelope
93
+ payload["call_id"] = callId
94
+ let module = self.module
95
+ PNMain.run {
96
+ PNBridge.shared.callPython(kind: "module", tag: 0, name: module, payload: PNJSON.encode(payload))
97
+ }
98
+ }
99
+ }
100
+
101
+ /// Called by the dispatcher once `call` returns: the inline result,
102
+ /// or `{"pending": true}` when the module will settle later.
103
+ func takeInlineResult() -> [String: Any] {
104
+ lock.lock()
105
+ defer { lock.unlock() }
106
+ returned = true
107
+ if let result = result { return result }
108
+ return ["pending": true]
109
+ }
110
+ }
111
+
112
+ /// Unsolicited module events: `callback("module", 0, module, {"event", "payload"})`.
113
+ public enum PNModuleEvents {
114
+ /// Emit `event` from `module` with `payload` on the main thread.
115
+ public static func emit(module: String, event: String, payload: [String: Any]) {
116
+ PNMain.run {
117
+ PNBridge.shared.callPython(kind: "module", tag: 0, name: module, payload: PNJSON.encode(["event": event, "payload": payload]))
118
+ }
119
+ }
120
+ }
121
+
122
+ /// Main-thread hop that runs inline when already on main.
123
+ public enum PNMain {
124
+ public static func run(_ block: @escaping () -> Void) {
125
+ if Thread.isMainThread {
126
+ block()
127
+ } else {
128
+ DispatchQueue.main.async(execute: block)
129
+ }
130
+ }
131
+ }
132
+
133
+ /// Routes `pn_bridge_call` to modules and shapes the reply envelope.
134
+ public final class PNModuleDispatcher {
135
+ public static let shared = PNModuleDispatcher()
136
+
137
+ private init() {}
138
+ private var pending: [Int64: PNPromise] = [:]
139
+ private let pendingLock = NSLock()
140
+
141
+ private func remove(_ callId: Int64) -> PNPromise? {
142
+ pendingLock.lock()
143
+ defer { pendingLock.unlock() }
144
+ return pending.removeValue(forKey: callId)
145
+ }
146
+
147
+ /// Dispatch one call. `envelope` is `{"call_id": n, "args": {...}}`.
148
+ public func call(module: String, method: String, envelope: [String: Any]) -> [String: Any] {
149
+ let callId = Int64(PNProps.int(envelope["call_id"]) ?? 0)
150
+ let args = PNProps.dict(envelope["args"]) ?? [:]
151
+ if method == "_pn_cancel" {
152
+ if let id = PNProps.int(args["call_id"]), let promise = remove(Int64(id)), promise.module == module { promise.cancel() }
153
+ return ["ok": true, "value": NSNull()]
154
+ }
155
+ guard let instance = PNRegistry.shared.module(named: module) else {
156
+ return ["ok": false, "error": "unknown native module '\(module)'", "code": "unknown_module"]
157
+ }
158
+ let promise = PNPromise(callId: callId, module: module, method: method)
159
+ if callId > 0 {
160
+ pendingLock.lock()
161
+ pending[callId] = promise
162
+ pendingLock.unlock()
163
+ promise.onFinished = { [weak self] in _ = self?.remove(callId) }
164
+ }
165
+ instance.call(method, args: args, promise: promise)
166
+ return promise.takeInlineResult()
167
+ }
168
+ }