@swiftbrowser/swift 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (393) hide show
  1. package/Package.swift +200 -9
  2. package/README.md +7 -9
  3. package/Sources/AVFoundation/AVFoundation.swift +131 -0
  4. package/Sources/AVFoundation/Asset.swift +487 -0
  5. package/Sources/AVFoundation/AudioSession.swift +144 -0
  6. package/Sources/AVFoundation/CMTime.swift +303 -0
  7. package/Sources/AVFoundation/Capture.swift +688 -0
  8. package/Sources/AVFoundation/Player.swift +749 -0
  9. package/Sources/AVFoundation/PlayerView.swift +117 -0
  10. package/Sources/AVKit/AVKit.swift +132 -0
  11. package/Sources/Accelerate/Accelerate.swift +122 -0
  12. package/Sources/AppIntents/AppIntents.swift +144 -0
  13. package/Sources/AppIntents/Parameters.swift +220 -0
  14. package/Sources/AppIntents/Shortcuts.swift +159 -0
  15. package/Sources/AudioToolbox/AudioToolbox.swift +55 -0
  16. package/Sources/AuthenticationServices/AuthenticationServices.swift +257 -0
  17. package/Sources/AuthenticationServices/Authorization.swift +251 -0
  18. package/Sources/Charts/Charts.swift +0 -4
  19. package/Sources/Combine/AnyCancellable.swift +65 -0
  20. package/Sources/Combine/AnyPublisher.swift +148 -0
  21. package/Sources/Combine/AnySubscriber.swift +230 -0
  22. package/Sources/Combine/Cancellable.swift +35 -0
  23. package/Sources/Combine/Codable.swift +29 -0
  24. package/Sources/Combine/CombineIdentifier.swift +45 -0
  25. package/Sources/Combine/Concurrency/ConcurrencyHelpers.swift +24 -0
  26. package/Sources/Combine/Concurrency/Future+Concurrency.swift +136 -0
  27. package/Sources/Combine/Concurrency/GENERATED-Publisher+Concurrency.swift +397 -0
  28. package/Sources/Combine/CurrentValueSubject.swift +252 -0
  29. package/Sources/Combine/CustomCombineIdentifierConvertible.swift +28 -0
  30. package/Sources/Combine/Future.swift +228 -0
  31. package/Sources/Combine/GENERATED-RootProtocols.swift +426 -0
  32. package/Sources/Combine/Helpers/ConduitBase.swift +40 -0
  33. package/Sources/Combine/Helpers/ConduitList.swift +59 -0
  34. package/Sources/Combine/Helpers/DebugHook.swift +177 -0
  35. package/Sources/Combine/Helpers/FilterProducer.swift +174 -0
  36. package/Sources/Combine/Helpers/Locking.swift +30 -0
  37. package/Sources/Combine/Helpers/PartialCompletion.swift +26 -0
  38. package/Sources/Combine/Helpers/ReduceProducer.swift +249 -0
  39. package/Sources/Combine/Helpers/SubjectSubscriber.swift +96 -0
  40. package/Sources/Combine/Helpers/SubscriberTap.swift +69 -0
  41. package/Sources/Combine/Helpers/SubscriptionStatus.swift +33 -0
  42. package/Sources/Combine/Helpers/SubscriptionTap.swift +33 -0
  43. package/Sources/Combine/Helpers/Utils.swift +30 -0
  44. package/Sources/Combine/Helpers/Violations.swift +33 -0
  45. package/Sources/Combine/ImmediateScheduler.swift +178 -0
  46. package/Sources/Combine/LICENSE +21 -0
  47. package/Sources/Combine/NotificationPublisher.swift +48 -0
  48. package/Sources/Combine/ObjectWillChange.swift +46 -0
  49. package/Sources/Combine/PassthroughSubject.swift +222 -0
  50. package/Sources/Combine/Publisher+Subscribe.swift +53 -0
  51. package/Sources/Combine/Publishers/Deferred.swift +48 -0
  52. package/Sources/Combine/Publishers/Empty.swift +54 -0
  53. package/Sources/Combine/Publishers/Fail.swift +42 -0
  54. package/Sources/Combine/Publishers/GENERATED-Publishers.Catch.swift +673 -0
  55. package/Sources/Combine/Publishers/GENERATED-Publishers.Encode.swift +388 -0
  56. package/Sources/Combine/Publishers/GENERATED-Publishers.MapKeyPath.swift +384 -0
  57. package/Sources/Combine/Publishers/Just.swift +313 -0
  58. package/Sources/Combine/Publishers/Optional.Publisher.swift +315 -0
  59. package/Sources/Combine/Publishers/Publishers.AllSatisfy.swift +217 -0
  60. package/Sources/Combine/Publishers/Publishers.AssertNoFailure.swift +158 -0
  61. package/Sources/Combine/Publishers/Publishers.Autoconnect.swift +193 -0
  62. package/Sources/Combine/Publishers/Publishers.Breakpoint.swift +231 -0
  63. package/Sources/Combine/Publishers/Publishers.Buffer.swift +356 -0
  64. package/Sources/Combine/Publishers/Publishers.Collect.swift +102 -0
  65. package/Sources/Combine/Publishers/Publishers.CollectByCount.swift +195 -0
  66. package/Sources/Combine/Publishers/Publishers.CompactMap.swift +241 -0
  67. package/Sources/Combine/Publishers/Publishers.Comparison.swift +371 -0
  68. package/Sources/Combine/Publishers/Publishers.Concatenate.swift +474 -0
  69. package/Sources/Combine/Publishers/Publishers.Contains.swift +293 -0
  70. package/Sources/Combine/Publishers/Publishers.Count.swift +87 -0
  71. package/Sources/Combine/Publishers/Publishers.Debounce.swift +332 -0
  72. package/Sources/Combine/Publishers/Publishers.Delay.swift +269 -0
  73. package/Sources/Combine/Publishers/Publishers.Drop.swift +155 -0
  74. package/Sources/Combine/Publishers/Publishers.DropUntilOutput.swift +283 -0
  75. package/Sources/Combine/Publishers/Publishers.DropWhile.swift +378 -0
  76. package/Sources/Combine/Publishers/Publishers.Filter.swift +248 -0
  77. package/Sources/Combine/Publishers/Publishers.First.swift +261 -0
  78. package/Sources/Combine/Publishers/Publishers.FlatMap.swift +533 -0
  79. package/Sources/Combine/Publishers/Publishers.HandleEvents.swift +260 -0
  80. package/Sources/Combine/Publishers/Publishers.IgnoreOutput.swift +109 -0
  81. package/Sources/Combine/Publishers/Publishers.Last.swift +252 -0
  82. package/Sources/Combine/Publishers/Publishers.MakeConnectable.swift +82 -0
  83. package/Sources/Combine/Publishers/Publishers.Map.swift +351 -0
  84. package/Sources/Combine/Publishers/Publishers.MapError.swift +140 -0
  85. package/Sources/Combine/Publishers/Publishers.MeasureInterval.swift +187 -0
  86. package/Sources/Combine/Publishers/Publishers.Multicast.swift +284 -0
  87. package/Sources/Combine/Publishers/Publishers.Output.swift +241 -0
  88. package/Sources/Combine/Publishers/Publishers.PrefixUntilOutput.swift +197 -0
  89. package/Sources/Combine/Publishers/Publishers.PrefixWhile.swift +176 -0
  90. package/Sources/Combine/Publishers/Publishers.Print.swift +206 -0
  91. package/Sources/Combine/Publishers/Publishers.ReceiveOn.swift +227 -0
  92. package/Sources/Combine/Publishers/Publishers.Reduce.swift +204 -0
  93. package/Sources/Combine/Publishers/Publishers.RemoveDuplicates.swift +264 -0
  94. package/Sources/Combine/Publishers/Publishers.ReplaceEmpty.swift +201 -0
  95. package/Sources/Combine/Publishers/Publishers.ReplaceError.swift +211 -0
  96. package/Sources/Combine/Publishers/Publishers.Retry.swift +274 -0
  97. package/Sources/Combine/Publishers/Publishers.Scan.swift +331 -0
  98. package/Sources/Combine/Publishers/Publishers.Sequence.swift +437 -0
  99. package/Sources/Combine/Publishers/Publishers.SetFailureType.swift +120 -0
  100. package/Sources/Combine/Publishers/Publishers.Share.swift +104 -0
  101. package/Sources/Combine/Publishers/Publishers.SubscribeOn.swift +197 -0
  102. package/Sources/Combine/Publishers/Publishers.SwitchToLatest.swift +444 -0
  103. package/Sources/Combine/Publishers/Publishers.Throttle.swift +359 -0
  104. package/Sources/Combine/Publishers/Publishers.Timeout.swift +301 -0
  105. package/Sources/Combine/Publishers/Publishers.Zip.swift +720 -0
  106. package/Sources/Combine/Publishers/Publishers.swift +13 -0
  107. package/Sources/Combine/Publishers/Record.swift +237 -0
  108. package/Sources/Combine/Publishers/Result.Publisher.swift +337 -0
  109. package/Sources/Combine/Result.swift +46 -0
  110. package/Sources/Combine/Scheduler.swift +89 -0
  111. package/Sources/Combine/Schedulers.swift +211 -0
  112. package/Sources/Combine/Subject+Void.swift +17 -0
  113. package/Sources/Combine/Subscriber+Void.swift +20 -0
  114. package/Sources/Combine/Subscribers/Subscribers.Assign.swift +175 -0
  115. package/Sources/Combine/Subscribers/Subscribers.Completion.swift +90 -0
  116. package/Sources/Combine/Subscribers/Subscribers.Demand.swift +476 -0
  117. package/Sources/Combine/Subscribers/Subscribers.Sink.swift +206 -0
  118. package/Sources/Combine/Subscribers/Subscribers.swift +9 -0
  119. package/Sources/Combine/Subscription.swift +22 -0
  120. package/Sources/Combine/Subscriptions.swift +42 -0
  121. package/Sources/Combine/SwiftUIObservation.swift +10 -0
  122. package/Sources/Combine/_Introspection.swift +107 -0
  123. package/Sources/CommonCrypto/CommonCrypto.swift +80 -0
  124. package/Sources/Compression/Compression.swift +365 -0
  125. package/Sources/Compression/Deflate.swift +407 -0
  126. package/Sources/Compression/Zlib.swift +179 -0
  127. package/Sources/CoreFoundationTypes/CoreFoundationTypes.swift +101 -0
  128. package/Sources/CoreGraphics/CoreGraphics.swift +411 -0
  129. package/Sources/CoreGraphics/ProjectionTransforms.swift +28 -0
  130. package/Sources/CoreHaptics/CoreHaptics.swift +399 -0
  131. package/Sources/CoreImage/CIColor.swift +41 -0
  132. package/Sources/CoreImage/CIContext.swift +31 -0
  133. package/Sources/CoreImage/CIDetector.swift +57 -0
  134. package/Sources/CoreImage/CIFilter.swift +243 -0
  135. package/Sources/CoreImage/CIFilterBuiltins.swift +136 -0
  136. package/Sources/CoreImage/CIImage.swift +162 -0
  137. package/Sources/CoreImage/QRCode.swift +393 -0
  138. package/Sources/CoreLocation/Coordinate.swift +30 -0
  139. package/Sources/CoreLocation/Location.swift +65 -0
  140. package/Sources/CoreML/Evaluation.swift +414 -0
  141. package/Sources/CoreML/FeatureValue.swift +266 -0
  142. package/Sources/CoreML/Model.swift +269 -0
  143. package/Sources/CoreML/MultiArray.swift +92 -0
  144. package/Sources/CoreML/Specification.swift +233 -0
  145. package/Sources/CoreText/CoreText.swift +64 -0
  146. package/Sources/CoreTransferable/CoreTransferable.swift +606 -0
  147. package/Sources/CryptoKit/ChaChaPoly.swift +258 -0
  148. package/Sources/CryptoKit/CryptoKit.swift +375 -0
  149. package/Sources/CryptoKit/Curve25519.swift +317 -0
  150. package/Sources/CryptoKit/P256.swift +321 -0
  151. package/Sources/FoundationBridge/AppStorage.swift +165 -0
  152. package/Sources/FoundationBridge/AssetResources.swift +90 -0
  153. package/Sources/FoundationBridge/AsyncBytes.swift +128 -0
  154. package/Sources/FoundationBridge/AsyncImage.swift +116 -0
  155. package/Sources/FoundationBridge/AttributedStrings.swift +16 -0
  156. package/Sources/{_FoundationCommon → FoundationBridge}/Bundle.swift +41 -4
  157. package/Sources/FoundationBridge/CombineExports.swift +8 -0
  158. package/Sources/FoundationBridge/CoreFoundation.swift +77 -0
  159. package/Sources/FoundationBridge/Dispatch.swift +458 -0
  160. package/Sources/FoundationBridge/EnvironmentFoundation.swift +41 -0
  161. package/Sources/FoundationBridge/FileImporter.swift +128 -0
  162. package/Sources/FoundationBridge/FileSystem.swift +68 -0
  163. package/Sources/FoundationBridge/FoundationBridge.swift +24 -72
  164. package/Sources/FoundationBridge/HostResolution.swift +31 -0
  165. package/Sources/FoundationBridge/ItemProvider.swift +655 -0
  166. package/Sources/FoundationBridge/KeyValueObserving.swift +138 -0
  167. package/Sources/FoundationBridge/Links.swift +127 -0
  168. package/Sources/FoundationBridge/ListFormatter.swift +32 -0
  169. package/Sources/FoundationBridge/Localization.swift +149 -0
  170. package/Sources/FoundationBridge/Locks.swift +50 -0
  171. package/Sources/FoundationBridge/Markdown.swift +527 -0
  172. package/Sources/FoundationBridge/MeasurementFormat.swift +95 -0
  173. package/Sources/FoundationBridge/NotificationCenter.swift +154 -0
  174. package/Sources/FoundationBridge/OpenURLHandlers.swift +60 -0
  175. package/Sources/FoundationBridge/Operation.swift +195 -0
  176. package/Sources/FoundationBridge/ProcessInfo.swift +32 -0
  177. package/Sources/FoundationBridge/RelativeDateTimeFormatter.swift +136 -0
  178. package/Sources/FoundationBridge/SceneStorage.swift +78 -0
  179. package/Sources/FoundationBridge/SecurityExports.swift +8 -0
  180. package/Sources/FoundationBridge/Selector.swift +226 -0
  181. package/Sources/FoundationBridge/StringComparators.swift +17 -0
  182. package/Sources/FoundationBridge/Table.swift +211 -0
  183. package/Sources/FoundationBridge/TextAttributes.swift +42 -0
  184. package/Sources/FoundationBridge/Timer.swift +6 -1
  185. package/Sources/FoundationBridge/URLSession.swift +718 -0
  186. package/Sources/{_FoundationCommon → FoundationBridge}/UserDefaults.swift +17 -4
  187. package/Sources/FoundationBridge/WebAuthRedirects.swift +227 -0
  188. package/Sources/FoundationBridge/WebSocket.swift +187 -0
  189. package/Sources/FoundationModels/FoundationModels.swift +1098 -0
  190. package/Sources/ImageIO/GIF.swift +72 -0
  191. package/Sources/ImageIO/ImageIO.swift +248 -0
  192. package/Sources/ImageIO/ImageTypes.swift +28 -0
  193. package/Sources/LinkPresentation/LinkPresentation.swift +249 -0
  194. package/Sources/LocalAuthentication/LAContext.swift +86 -0
  195. package/Sources/MapKit/Geometry.swift +223 -0
  196. package/Sources/MapKit/Map.swift +638 -0
  197. package/Sources/MapKit/MapView.swift +229 -0
  198. package/Sources/MobileCoreServices/MobileCoreServices.swift +28 -0
  199. package/Sources/NaturalLanguage/LanguageIdentifier.swift +317 -0
  200. package/Sources/NaturalLanguage/NaturalLanguage.swift +135 -0
  201. package/Sources/OSLog/OSLog.swift +6 -0
  202. package/Sources/Photos/Photos.swift +572 -0
  203. package/Sources/PhotosUI/PhotosPicker.swift +248 -0
  204. package/Sources/PhotosUI/PhotosPickerItem.swift +261 -0
  205. package/Sources/QuickLook/QuickLook.swift +168 -0
  206. package/Sources/SafariServices/SafariServices.swift +106 -0
  207. package/Sources/Security/Security.swift +332 -0
  208. package/Sources/StoreKit/AppStore.swift +207 -0
  209. package/Sources/StoreKit/Products.swift +280 -0
  210. package/Sources/StoreKit/RequestReview.swift +28 -0
  211. package/Sources/StoreKit/StoreKit1.swift +486 -0
  212. package/Sources/StoreKit/StoreViews.swift +428 -0
  213. package/Sources/StoreKit/Transactions.swift +197 -0
  214. package/Sources/SwiftData/Macros.swift +58 -0
  215. package/Sources/SwiftData/ModelActor.swift +38 -0
  216. package/Sources/SwiftData/ModelContainer.swift +129 -0
  217. package/Sources/SwiftData/ModelContext.swift +451 -0
  218. package/Sources/SwiftData/PersistentModel.swift +238 -0
  219. package/Sources/SwiftData/Properties.swift +261 -0
  220. package/Sources/SwiftData/Query.swift +256 -0
  221. package/Sources/SwiftData/Schema.swift +192 -0
  222. package/Sources/SwiftData/Store.swift +146 -0
  223. package/Sources/SwiftData/StringPredicates.swift +125 -0
  224. package/Sources/SwiftDataMacros/EntryMacro.swift +82 -0
  225. package/Sources/SwiftDataMacros/GenerableMacro.swift +183 -0
  226. package/Sources/SwiftDataMacros/ModelActorMacro.swift +46 -0
  227. package/Sources/SwiftDataMacros/ModelMacro.swift +260 -0
  228. package/Sources/SwiftDataMacros/Plugin.swift +18 -0
  229. package/Sources/SwiftDataMacros/PredicateMacro.swift +1244 -0
  230. package/Sources/SwiftUI/API/Accessibility.swift +55 -6
  231. package/Sources/SwiftUI/API/AccessibilityExtras.swift +33 -0
  232. package/Sources/SwiftUI/API/AccessibilityFocus.swift +63 -0
  233. package/Sources/SwiftUI/API/Alert.swift +1 -1
  234. package/Sources/SwiftUI/API/AlignmentGuides.swift +68 -0
  235. package/Sources/SwiftUI/API/Animation.swift +37 -5
  236. package/Sources/SwiftUI/API/App.swift +31 -1
  237. package/Sources/SwiftUI/API/BindingCollections.swift +35 -0
  238. package/Sources/SwiftUI/API/BindingForEach.swift +27 -0
  239. package/Sources/SwiftUI/API/Bitmaps.swift +254 -0
  240. package/Sources/SwiftUI/API/ButtonStyles.swift +96 -0
  241. package/Sources/SwiftUI/API/Canvas.swift +337 -0
  242. package/Sources/SwiftUI/API/Color.swift +104 -28
  243. package/Sources/SwiftUI/API/ColorEffects.swift +52 -0
  244. package/Sources/SwiftUI/API/ColorPickerAndLabeledContent.swift +138 -0
  245. package/Sources/SwiftUI/API/ColorSpaces.swift +161 -0
  246. package/Sources/SwiftUI/API/Commands.swift +106 -0
  247. package/Sources/SwiftUI/API/ContainerBackground.swift +28 -0
  248. package/Sources/SwiftUI/API/ContentShape.swift +33 -0
  249. package/Sources/SwiftUI/API/ContentSizeCategory.swift +35 -0
  250. package/Sources/SwiftUI/API/ContentUnavailable.swift +101 -0
  251. package/Sources/SwiftUI/API/Controls.swift +59 -27
  252. package/Sources/SwiftUI/API/CustomLayout.swift +49 -10
  253. package/Sources/SwiftUI/API/CustomToolbarContent.swift +46 -0
  254. package/Sources/SwiftUI/API/Decorations.swift +1 -1
  255. package/Sources/SwiftUI/API/EditMode.swift +25 -82
  256. package/Sources/SwiftUI/API/Effects.swift +125 -10
  257. package/Sources/SwiftUI/API/Entry.swift +8 -0
  258. package/Sources/SwiftUI/API/Environment.swift +100 -9
  259. package/Sources/SwiftUI/API/EnvironmentSendable.swift +6 -0
  260. package/Sources/SwiftUI/API/Focus.swift +11 -3
  261. package/Sources/SwiftUI/API/Gauge.swift +26 -10
  262. package/Sources/SwiftUI/API/GeometryEffects.swift +232 -0
  263. package/Sources/SwiftUI/API/GeometryReader.swift +13 -2
  264. package/Sources/SwiftUI/API/GestureState.swift +65 -0
  265. package/Sources/SwiftUI/API/Gestures.swift +1 -1
  266. package/Sources/SwiftUI/API/Glass.swift +127 -0
  267. package/Sources/SwiftUI/API/Grid.swift +1 -1
  268. package/Sources/SwiftUI/API/GroupBoxAndControlGroup.swift +130 -0
  269. package/Sources/SwiftUI/API/KeyPress.swift +54 -0
  270. package/Sources/SwiftUI/API/LabelStyle.swift +13 -6
  271. package/Sources/SwiftUI/API/Layout.swift +71 -12
  272. package/Sources/SwiftUI/API/LazyStacks.swift +49 -0
  273. package/Sources/SwiftUI/API/LegacyAlert.swift +97 -0
  274. package/Sources/SwiftUI/API/Lists.swift +61 -6
  275. package/Sources/SwiftUI/API/Menu.swift +8 -1
  276. package/Sources/SwiftUI/API/Modifiers.swift +88 -12
  277. package/Sources/SwiftUI/API/MoreModifiers.swift +100 -0
  278. package/Sources/SwiftUI/API/Namespace.swift +44 -0
  279. package/Sources/SwiftUI/API/Navigation.swift +24 -9
  280. package/Sources/SwiftUI/API/NavigationDestinationBindings.swift +55 -0
  281. package/Sources/SwiftUI/API/ObservableObject.swift +16 -2
  282. package/Sources/SwiftUI/API/Path.swift +543 -0
  283. package/Sources/SwiftUI/API/Phase8Controls.swift +48 -31
  284. package/Sources/SwiftUI/API/PhaseAnimator.swift +130 -0
  285. package/Sources/SwiftUI/API/Picker.swift +14 -4
  286. package/Sources/SwiftUI/API/PointerAndKeyboard.swift +77 -0
  287. package/Sources/SwiftUI/API/Popover.swift +41 -0
  288. package/Sources/SwiftUI/API/Preferences.swift +262 -0
  289. package/Sources/SwiftUI/API/Presentation.swift +17 -4
  290. package/Sources/SwiftUI/API/PresentationExtras.swift +60 -0
  291. package/Sources/SwiftUI/API/Primitives.swift +168 -39
  292. package/Sources/SwiftUI/API/Publishers.swift +4 -4
  293. package/Sources/SwiftUI/API/Redaction.swift +75 -0
  294. package/Sources/SwiftUI/API/RowActions.swift +3 -2
  295. package/Sources/SwiftUI/API/Scenes.swift +131 -0
  296. package/Sources/SwiftUI/API/ScrollModifiers.swift +132 -0
  297. package/Sources/SwiftUI/API/ScrollTargets.swift +3 -3
  298. package/Sources/SwiftUI/API/ScrollViewReader.swift +144 -0
  299. package/Sources/SwiftUI/API/SectionSupplements.swift +86 -0
  300. package/Sources/SwiftUI/API/Shape.swift +214 -0
  301. package/Sources/SwiftUI/API/ShapeStyle.swift +187 -8
  302. package/Sources/SwiftUI/API/ShapeTrim.swift +78 -0
  303. package/Sources/SwiftUI/API/Sharing.swift +193 -0
  304. package/Sources/SwiftUI/API/StackLayouts.swift +199 -0
  305. package/Sources/SwiftUI/API/State.swift +18 -1
  306. package/Sources/SwiftUI/API/StatusGaps.swift +121 -0
  307. package/Sources/SwiftUI/API/SymbolEffects.swift +203 -0
  308. package/Sources/SwiftUI/API/TabContent.swift +841 -0
  309. package/Sources/SwiftUI/API/Table.swift +382 -0
  310. package/Sources/SwiftUI/API/Tabs.swift +148 -4
  311. package/Sources/SwiftUI/API/Tasks.swift +2 -2
  312. package/Sources/SwiftUI/API/Text.swift +386 -0
  313. package/Sources/SwiftUI/API/TextDates.swift +61 -0
  314. package/Sources/SwiftUI/API/TextDecorations.swift +52 -0
  315. package/Sources/SwiftUI/API/TextFieldAxis.swift +103 -0
  316. package/Sources/SwiftUI/API/TextFieldCommit.swift +52 -0
  317. package/Sources/SwiftUI/API/Toolbar.swift +52 -16
  318. package/Sources/SwiftUI/API/Transfer.swift +145 -0
  319. package/Sources/SwiftUI/API/UnevenRoundedRectangle.swift +66 -0
  320. package/Sources/SwiftUI/API/Video.swift +64 -0
  321. package/Sources/SwiftUI/API/View.swift +46 -2
  322. package/Sources/SwiftUI/API/ViewModifier.swift +4 -1
  323. package/Sources/SwiftUI/API/VisualEffect.swift +74 -1
  324. package/Sources/SwiftUI/API/Windows.swift +25 -0
  325. package/Sources/SwiftUI/Charts/Axis.swift +7 -1
  326. package/Sources/SwiftUI/Charts/AxisFormatsAndRanges.swift +68 -0
  327. package/Sources/SwiftUI/Charts/Chart.swift +17 -3
  328. package/Sources/SwiftUI/Charts/ChartProxy.swift +57 -0
  329. package/Sources/SwiftUI/Localization/Localization.swift +524 -0
  330. package/Sources/SwiftUI/Localization/PluralRules.swift +127 -0
  331. package/Sources/SwiftUI/Runtime/Executor.swift +2 -0
  332. package/Sources/SwiftUI/Runtime/Exports.swift +37 -1
  333. package/Sources/SwiftUI/Runtime/ImageSource.swift +310 -0
  334. package/Sources/SwiftUI/Runtime/JSON.swift +76 -4
  335. package/Sources/SwiftUI/Runtime/LazyRows.swift +99 -0
  336. package/Sources/SwiftUI/Runtime/Measurements.swift +140 -0
  337. package/Sources/SwiftUI/Runtime/Ops.swift +58 -12
  338. package/Sources/SwiftUI/Runtime/Reconciler.swift +10 -1
  339. package/Sources/SwiftUI/Runtime/Replay.swift +382 -0
  340. package/Sources/SwiftUI/Runtime/Runtime.swift +977 -198
  341. package/Sources/SwiftUI/Runtime/Sameness.swift +152 -0
  342. package/Sources/SwiftUI/Runtime/ViewPath.swift +89 -0
  343. package/Sources/SwiftUI/Runtime/ViewTraits.swift +175 -0
  344. package/Sources/SwiftUI/Runtime/WebAuthHeadless.swift +49 -0
  345. package/Sources/Symbols/Symbols.swift +25 -0
  346. package/Sources/Translation/Translation.swift +423 -0
  347. package/Sources/UIKit/Activity.swift +213 -0
  348. package/Sources/UIKit/Alerts.swift +69 -0
  349. package/Sources/UIKit/AppDelegate.swift +222 -0
  350. package/Sources/UIKit/Appearance.swift +84 -0
  351. package/Sources/UIKit/ColorConversions.swift +70 -0
  352. package/Sources/UIKit/Controls.swift +225 -0
  353. package/Sources/UIKit/Device.swift +200 -0
  354. package/Sources/UIKit/Feedback.swift +65 -0
  355. package/Sources/UIKit/Font.swift +307 -0
  356. package/Sources/UIKit/Graphics.swift +204 -0
  357. package/Sources/UIKit/HostingController.swift +185 -0
  358. package/Sources/UIKit/Identity.swift +24 -0
  359. package/Sources/UIKit/Introspectable.swift +510 -0
  360. package/Sources/UIKit/Pasteboard.swift +363 -0
  361. package/Sources/UIKit/PhotoAlbum.swift +81 -0
  362. package/Sources/UIKit/RemoteNotifications.swift +15 -0
  363. package/Sources/UIKit/Representable.swift +288 -0
  364. package/Sources/UIKit/SafeArea.swift +12 -0
  365. package/Sources/UIKit/SceneDelegates.swift +132 -0
  366. package/Sources/UIKit/Scenes.swift +40 -0
  367. package/Sources/UIKit/ScrollView.swift +157 -0
  368. package/Sources/UIKit/ShortcutItems.swift +67 -0
  369. package/Sources/UIKit/SymbolConfiguration.swift +124 -0
  370. package/Sources/UIKit/TargetAction.swift +123 -0
  371. package/Sources/UIKit/TextPaste.swift +161 -0
  372. package/Sources/UIKit/TextView.swift +538 -0
  373. package/Sources/UIKit/UIKit.swift +292 -0
  374. package/Sources/UIKit/UIView.swift +855 -0
  375. package/Sources/UIKit/ViewTranslation.swift +311 -0
  376. package/Sources/UniformTypeIdentifiers/UTType.swift +196 -0
  377. package/Sources/UserNotifications/Content.swift +370 -0
  378. package/Sources/UserNotifications/NotificationCenter.swift +309 -0
  379. package/Sources/WeatherKit/WeatherKit.swift +228 -0
  380. package/Sources/WebKit/WebKit.swift +245 -0
  381. package/Sources/os/os.swift +376 -0
  382. package/Sources/zlib/zlib.swift +6 -0
  383. package/index.d.ts +1 -1
  384. package/index.mjs +2 -2
  385. package/manifest.d.mts +2 -0
  386. package/manifest.mjs +41 -0
  387. package/package.json +5 -3
  388. package/sync-sources.mjs +21 -7
  389. package/Sources/FoundationLite/Common.swift +0 -3
  390. package/Sources/FoundationLite/FoundationLite.swift +0 -171
  391. package/Sources/FoundationLite/Interaction.swift +0 -146
  392. package/Sources/FoundationLite/Timer.swift +0 -48
  393. package/Sources/_FoundationCommon/_FoundationCommon.swift +0 -10
@@ -23,6 +23,42 @@ public func _sb_ops_clear() {
23
23
  MainActor.assumeIsolated { _BufferedHost.shared.clear() }
24
24
  }
25
25
 
26
+ /// The number of byte buffers beside the pending ops (`{"$bytes": n}` values).
27
+ @_cdecl("sb_bytes_count")
28
+ public func _sb_bytes_count() -> Int32 {
29
+ MainActor.assumeIsolated { Int32(_BufferedHost.shared.bytes.count) }
30
+ }
31
+
32
+ /// Where the pending ops' `n`th byte buffer starts in linear memory.
33
+ @_cdecl("sb_bytes_ptr")
34
+ public func _sb_bytes_ptr(_ index: Int32) -> Int32 {
35
+ MainActor.assumeIsolated {
36
+ let items = _BufferedHost.shared.bytes
37
+ guard items.indices.contains(Int(index)) else { return 0 }
38
+ return Int32(Int(bitPattern: items[Int(index)].baseAddress))
39
+ }
40
+ }
41
+
42
+ /// The length of the pending ops' `n`th byte buffer.
43
+ @_cdecl("sb_bytes_len")
44
+ public func _sb_bytes_len(_ index: Int32) -> Int32 {
45
+ MainActor.assumeIsolated {
46
+ let items = _BufferedHost.shared.bytes
47
+ guard items.indices.contains(Int(index)) else { return 0 }
48
+ return Int32(items[Int(index)].count)
49
+ }
50
+ }
51
+
52
+ /// Takes a buffer from `sb_alloc` that the renderer filled with bytes for
53
+ /// the next event, and answers the `n` the event names them by (`{"$bytes": n}`).
54
+ @_cdecl("sb_bytes")
55
+ public func _sb_bytes(_ pointer: Int32, _ length: Int32) -> Int32 {
56
+ guard let base = UnsafeMutablePointer<UInt8>(bitPattern: Int(pointer)) else { return 0 }
57
+ let bytes = Array(UnsafeBufferPointer(start: base, count: Int(length)))
58
+ base.deallocate()
59
+ return MainActor.assumeIsolated { Int32(_BytesInbox.shared.add(bytes)) }
60
+ }
61
+
26
62
  /// Allocates a buffer the renderer fills before calling `sb_event_json`.
27
63
  @_cdecl("sb_alloc")
28
64
  public func _sb_alloc(_ length: Int32) -> Int32 {
@@ -41,7 +77,7 @@ public func _sb_event_json(_ pointer: Int32, _ length: Int32) {
41
77
  print("[SwiftUI] ignoring malformed event: \(text)")
42
78
  return
43
79
  }
44
- _Runtime.shared.dispatch(event: event)
80
+ _Runtime.shared.dispatch(event: _BytesInbox.shared.resolve(event))
45
81
  }
46
82
  }
47
83
 
@@ -0,0 +1,310 @@
1
+ import Foundation
2
+
3
+ // Bitmaps (`UIImage`, `CGImage`, Core Image's `CIImage`) never hold decoded
4
+ // pixels in Swift. Each is a recipe: a source (an encoded file's bytes, or a
5
+ // small pixel buffer a generator made) plus the Core Image steps applied to
6
+ // it, in order. The page decodes the source and runs the steps when it draws
7
+ // the image (Web/src/bitmaps.ts, docs/ops-protocol.md, "Bitmaps and Core
8
+ // Image"). Swift only needs sizes, which it reads from the file's header, so
9
+ // `UIImage(data:)` stays synchronous as on iOS while the browser decodes
10
+ // asynchronously.
11
+
12
+ /// What an encoded image's header says: its media type and its pixel size,
13
+ /// with the EXIF (JPEG) or `irot` (HEIF) rotation applied, as browsers draw it.
14
+ public struct _ImageHeader: Equatable, Sendable {
15
+ public let mime: String
16
+ public let width: Int
17
+ public let height: Int
18
+
19
+ public init(mime: String, width: Int, height: Int) {
20
+ self.mime = mime
21
+ self.width = width
22
+ self.height = height
23
+ }
24
+
25
+ /// The header of a PNG, JPEG, GIF, WebP, BMP, HEIC or AVIF file; nil for
26
+ /// anything else, or a header too short or malformed to give a size.
27
+ public static func parse(_ bytes: [UInt8]) -> _ImageHeader? {
28
+ let reader = _ByteReader(bytes)
29
+ let parsed = parsePNG(reader) ?? parseJPEG(reader) ?? parseGIF(reader) ?? parseWebP(reader)
30
+ ?? parseBMP(reader) ?? parseHEIF(reader)
31
+ guard let parsed, parsed.width > 0, parsed.height > 0 else { return nil }
32
+ return parsed
33
+ }
34
+
35
+ private static func parsePNG(_ r: _ByteReader) -> _ImageHeader? {
36
+ guard r.matches([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A], at: 0), r.matches(Array("IHDR".utf8), at: 12),
37
+ let width = r.u32be(16), let height = r.u32be(20) else { return nil }
38
+ return _ImageHeader(mime: "image/png", width: Int(width), height: Int(height))
39
+ }
40
+
41
+ private static func parseGIF(_ r: _ByteReader) -> _ImageHeader? {
42
+ guard r.matches(Array("GIF87a".utf8), at: 0) || r.matches(Array("GIF89a".utf8), at: 0),
43
+ let width = r.u16le(6), let height = r.u16le(8) else { return nil }
44
+ return _ImageHeader(mime: "image/gif", width: Int(width), height: Int(height))
45
+ }
46
+
47
+ private static func parseBMP(_ r: _ByteReader) -> _ImageHeader? {
48
+ guard r.matches(Array("BM".utf8), at: 0), let width = r.u32le(18), let height = r.u32le(22) else { return nil }
49
+ // The height is negative for a top-down bitmap.
50
+ return _ImageHeader(mime: "image/bmp", width: Int(Int32(bitPattern: width).magnitude), height: Int(Int32(bitPattern: height).magnitude))
51
+ }
52
+
53
+ private static func parseWebP(_ r: _ByteReader) -> _ImageHeader? {
54
+ guard r.matches(Array("RIFF".utf8), at: 0), r.matches(Array("WEBP".utf8), at: 8) else { return nil }
55
+ if r.matches(Array("VP8 ".utf8), at: 12) {
56
+ // Lossy: a key frame's start code, then 14-bit width and height.
57
+ guard r.matches([0x9D, 0x01, 0x2A], at: 23), let width = r.u16le(26), let height = r.u16le(28) else { return nil }
58
+ return _ImageHeader(mime: "image/webp", width: Int(width & 0x3FFF), height: Int(height & 0x3FFF))
59
+ }
60
+ if r.matches(Array("VP8L".utf8), at: 12) {
61
+ // Lossless: a signature byte, then width - 1 and height - 1 in 14 bits each.
62
+ guard r.byte(20) == 0x2F, let b0 = r.byte(21), let b1 = r.byte(22), let b2 = r.byte(23), let b3 = r.byte(24) else { return nil }
63
+ let width = 1 + (Int(b0) | (Int(b1 & 0x3F) << 8))
64
+ let height = 1 + ((Int(b1) >> 6) | (Int(b2) << 2) | (Int(b3 & 0x0F) << 10))
65
+ return _ImageHeader(mime: "image/webp", width: width, height: height)
66
+ }
67
+ if r.matches(Array("VP8X".utf8), at: 12) {
68
+ // Extended: the canvas size, minus one, in 24 bits each.
69
+ guard let width = r.u24le(24), let height = r.u24le(27) else { return nil }
70
+ return _ImageHeader(mime: "image/webp", width: Int(width) + 1, height: Int(height) + 1)
71
+ }
72
+ return nil
73
+ }
74
+
75
+ private static func parseJPEG(_ r: _ByteReader) -> _ImageHeader? {
76
+ guard r.matches([0xFF, 0xD8], at: 0) else { return nil }
77
+ var index = 2
78
+ var orientation = 1
79
+ while index + 4 <= r.count {
80
+ guard r.byte(index) == 0xFF, let marker = r.byte(index + 1) else { return nil }
81
+ if marker == 0xFF { index += 1; continue } // fill byte
82
+ index += 2
83
+ // Markers without a length: TEM, RSTn, SOI.
84
+ if marker == 0x01 || (0xD0...0xD8).contains(marker) { continue }
85
+ // End of image or start of scan before any frame header: no size.
86
+ if marker == 0xD9 || marker == 0xDA { return nil }
87
+ guard let length = r.u16be(index), length >= 2 else { return nil }
88
+ if marker == 0xE1, let value = exifOrientation(r, segment: index + 2, length: Int(length) - 2) {
89
+ orientation = value
90
+ }
91
+ // SOF0...SOF15, except DHT (C4), JPG (C8) and DAC (CC).
92
+ if (0xC0...0xCF).contains(marker), marker != 0xC4, marker != 0xC8, marker != 0xCC {
93
+ guard let height = r.u16be(index + 3), let width = r.u16be(index + 5) else { return nil }
94
+ // Orientations 5 to 8 turn the picture a quarter: browsers draw it (and
95
+ // UIImage reports it) with width and height swapped.
96
+ let turned = orientation >= 5 && orientation <= 8
97
+ return _ImageHeader(mime: "image/jpeg", width: Int(turned ? height : width), height: Int(turned ? width : height))
98
+ }
99
+ index += Int(length)
100
+ }
101
+ return nil
102
+ }
103
+
104
+ /// The orientation tag (0x0112) of an APP1 Exif segment's first IFD.
105
+ private static func exifOrientation(_ r: _ByteReader, segment: Int, length: Int) -> Int? {
106
+ guard length > 14, r.matches(Array("Exif".utf8) + [0, 0], at: segment) else { return nil }
107
+ let tiff = segment + 6
108
+ let little: Bool
109
+ if r.matches(Array("II".utf8), at: tiff) { little = true }
110
+ else if r.matches(Array("MM".utf8), at: tiff) { little = false }
111
+ else { return nil }
112
+ let u16 = { (at: Int) in little ? r.u16le(at) : r.u16be(at) }
113
+ let u32 = { (at: Int) in little ? r.u32le(at) : r.u32be(at) }
114
+ guard let ifd = u32(tiff + 4), let count = u16(tiff + Int(ifd)) else { return nil }
115
+ for entry in 0..<Int(count) {
116
+ let at = tiff + Int(ifd) + 2 + entry * 12
117
+ guard at + 12 <= segment + length, let tag = u16(at) else { return nil }
118
+ if tag == 0x0112, let value = u16(at + 8) { return Int(value) }
119
+ }
120
+ return nil
121
+ }
122
+
123
+ /// HEIC and AVIF (ISO base media files): the largest `ispe` (image spatial
124
+ /// extent) property is the primary image's size; an `irot` of a quarter
125
+ /// or three quarters swaps it.
126
+ private static func parseHEIF(_ r: _ByteReader) -> _ImageHeader? {
127
+ guard r.matches(Array("ftyp".utf8), at: 4), let boxSize = r.u32be(0) else { return nil }
128
+ let brands = Set(stride(from: 8, to: min(Int(boxSize), r.count) - 3, by: 4).compactMap { at -> String? in
129
+ guard at != 12, let bytes = r.slice(at, 4) else { return nil } // 12: the minor version
130
+ return String(decoding: bytes, as: UTF8.self)
131
+ })
132
+ let isAVIF = !brands.isDisjoint(with: ["avif", "avis"])
133
+ guard isAVIF || !brands.isDisjoint(with: ["heic", "heix", "hevc", "hevx", "heim", "heis", "mif1", "msf1"]) else { return nil }
134
+ var best: (width: Int, height: Int)?
135
+ var turned = false
136
+ let ispe = Array("ispe".utf8), irot = Array("irot".utf8)
137
+ // The `meta` box is near the start; the image data comes after it.
138
+ let limit = min(r.count, 1 << 20)
139
+ var at = 4
140
+ while at + 5 <= limit {
141
+ if r.matches(ispe, at: at), let width = r.u32be(at + 8), let height = r.u32be(at + 12) {
142
+ if Int(width) * Int(height) > (best.map { $0.width * $0.height } ?? 0) { best = (Int(width), Int(height)) }
143
+ at += 16
144
+ continue
145
+ }
146
+ if r.matches(irot, at: at), let angle = r.byte(at + 4) { turned = (angle & 0x03) % 2 == 1 }
147
+ at += 1
148
+ }
149
+ guard let best else { return nil }
150
+ return _ImageHeader(mime: isAVIF ? "image/avif" : "image/heic",
151
+ width: turned ? best.height : best.width, height: turned ? best.width : best.height)
152
+ }
153
+ }
154
+
155
+ /// Bounds-checked reads from a byte array; every read past the end is nil.
156
+ struct _ByteReader {
157
+ let bytes: [UInt8]
158
+ init(_ bytes: [UInt8]) { self.bytes = bytes }
159
+
160
+ var count: Int { bytes.count }
161
+ func byte(_ at: Int) -> UInt8? { at >= 0 && at < bytes.count ? bytes[at] : nil }
162
+ func slice(_ at: Int, _ length: Int) -> ArraySlice<UInt8>? {
163
+ at >= 0 && length >= 0 && at + length <= bytes.count ? bytes[at..<(at + length)] : nil
164
+ }
165
+ func matches(_ pattern: [UInt8], at: Int) -> Bool { slice(at, pattern.count).map { $0.elementsEqual(pattern) } ?? false }
166
+ func u16be(_ at: Int) -> UInt16? { slice(at, 2).map { UInt16($0[at]) << 8 | UInt16($0[at + 1]) } }
167
+ func u16le(_ at: Int) -> UInt16? { slice(at, 2).map { UInt16($0[at + 1]) << 8 | UInt16($0[at]) } }
168
+ func u24le(_ at: Int) -> UInt32? { slice(at, 3).map { UInt32($0[at + 2]) << 16 | UInt32($0[at + 1]) << 8 | UInt32($0[at]) } }
169
+ func u32be(_ at: Int) -> UInt32? {
170
+ slice(at, 4).map { UInt32($0[at]) << 24 | UInt32($0[at + 1]) << 16 | UInt32($0[at + 2]) << 8 | UInt32($0[at + 3]) }
171
+ }
172
+ func u32le(_ at: Int) -> UInt32? {
173
+ slice(at, 4).map { UInt32($0[at + 3]) << 24 | UInt32($0[at + 2]) << 16 | UInt32($0[at + 1]) << 8 | UInt32($0[at]) }
174
+ }
175
+ }
176
+
177
+ /// Where a bitmap's pixels come from: an encoded image file, or a pixel
178
+ /// buffer (one gray or four RGBA bytes per pixel, rows from the top). The
179
+ /// page receives it once, in an `imagedata` op, under its `key` (a hash of
180
+ /// the content, so the same bytes are sent once however many images use them).
181
+ public final class _ImageSource: Equatable, @unchecked Sendable {
182
+ public enum PixelFormat: String, Sendable {
183
+ case gray, rgba
184
+ }
185
+
186
+ enum Payload {
187
+ case encoded([UInt8], mime: String)
188
+ case pixels([UInt8], PixelFormat)
189
+ }
190
+
191
+ public let key: String
192
+ /// The size in pixels, as the page draws it (orientation applied).
193
+ public let width: Int
194
+ public let height: Int
195
+ let payload: Payload
196
+
197
+ /// An encoded image file; nil when its header is not one the page can draw.
198
+ public init?(encoded bytes: [UInt8]) {
199
+ guard let header = _ImageHeader.parse(bytes) else { return nil }
200
+ key = "f" + _fnv1a(bytes)
201
+ width = header.width
202
+ height = header.height
203
+ payload = .encoded(bytes, mime: header.mime)
204
+ }
205
+
206
+ /// A pixel buffer: `width * height` bytes for `.gray`, four times that
207
+ /// (straight, not premultiplied, alpha) for `.rgba`.
208
+ public init(pixels: [UInt8], width: Int, height: Int, format: PixelFormat) {
209
+ precondition(pixels.count == width * height * (format == .gray ? 1 : 4), "pixel buffer size does not match \(width)×\(height) \(format)")
210
+ key = "p" + _fnv1a(pixels + Array("\(width)x\(height)\(format.rawValue)".utf8))
211
+ self.width = width
212
+ self.height = height
213
+ payload = .pixels(pixels, format)
214
+ }
215
+
216
+ public static func == (lhs: _ImageSource, rhs: _ImageSource) -> Bool { lhs.key == rhs.key }
217
+
218
+ /// The media type of an encoded source; nil for pixels.
219
+ public var mime: String? {
220
+ if case .encoded(_, let mime) = payload { return mime }
221
+ return nil
222
+ }
223
+
224
+ /// The `imagedata` op's fields.
225
+ var json: _JSON {
226
+ var fields: [String: _JSON] = ["key": .string(key), "width": .number(Double(width)), "height": .number(Double(height))]
227
+ switch payload {
228
+ case .encoded(let bytes, let mime):
229
+ fields["mime"] = .string(mime)
230
+ fields["data"] = .bytes(bytes)
231
+ case .pixels(let bytes, let format):
232
+ fields["format"] = .string(format.rawValue)
233
+ fields["pixels"] = .bytes(bytes)
234
+ }
235
+ return .object(fields)
236
+ }
237
+ }
238
+
239
+ /// A bitmap as the page will draw it: its source and the Core Image steps
240
+ /// applied to it. `extent` is Core Image's: in source pixels, origin at the
241
+ /// bottom left, possibly infinite (`clampedToExtent()`). A `CGImage`'s
242
+ /// recipe ends at its own pixel grid: its extent is `(0, 0, width, height)`.
243
+ public struct _ImageRecipe: Equatable, Sendable {
244
+ /// Nil for an empty image (`UIImage()`, `CIImage.empty()`).
245
+ public let source: _ImageSource?
246
+ /// Each step is an object whose `op` names it (docs/ops-protocol.md).
247
+ public let steps: [_JSON]
248
+ public let extent: CGRect
249
+ /// The frame of an animated source (a GIF) the steps start from, whole as
250
+ /// the file's earlier frames leave the canvas; nil for the file as it is
251
+ /// (its first frame).
252
+ public let frame: Int?
253
+
254
+ public init(source: _ImageSource?, steps: [_JSON] = [], extent: CGRect, frame: Int? = nil) {
255
+ self.source = source
256
+ self.steps = steps
257
+ self.extent = extent
258
+ self.frame = frame
259
+ }
260
+
261
+ /// The source on its own: extent `(0, 0, width, height)`.
262
+ public init(source: _ImageSource, frame: Int? = nil) {
263
+ self.init(source: source, extent: CGRect(x: 0, y: 0, width: Double(source.width), height: Double(source.height)), frame: frame)
264
+ }
265
+
266
+ public static let empty = _ImageRecipe(source: nil, extent: .null)
267
+
268
+ /// This recipe followed by `step`, which gives the image `extent`.
269
+ public func adding(_ step: _JSON, extent: CGRect) -> _ImageRecipe {
270
+ _ImageRecipe(source: source, steps: steps + [step], extent: extent, frame: frame)
271
+ }
272
+
273
+ /// The image inside `rect` (Core Image coordinates) as a bitmap of its own,
274
+ /// `(0, 0, width, height)` in pixels: clear where the image does not reach
275
+ /// (`CIContext.createCGImage(_:from:)`). Nil for an infinite or empty rect.
276
+ public func rendered(in rect: CGRect) -> _ImageRecipe? {
277
+ guard !rect.isInfinite, !rect.isNull else { return nil }
278
+ let box = rect.integral
279
+ guard box.width >= 1, box.height >= 1 else { return nil }
280
+ let step: _JSON = .object(["op": .string("render"), "x": .number(box.minX), "y": .number(box.minY),
281
+ "width": .number(box.width), "height": .number(box.height)])
282
+ return adding(step, extent: CGRect(x: 0, y: 0, width: box.width, height: box.height))
283
+ }
284
+
285
+ /// The `bitmap` prop of an `image` element: the source's key, the frame
286
+ /// of an animated one, and the steps; `width` / `height` are the result's
287
+ /// size in pixels.
288
+ var json: _JSON {
289
+ let finite = !extent.isNull && !extent.isInfinite
290
+ var fields: [String: _JSON] = [
291
+ "source": source.map { .string($0.key) } ?? .null,
292
+ "steps": .array(steps),
293
+ "width": .number(finite ? extent.width : 0),
294
+ "height": .number(finite ? extent.height : 0),
295
+ ]
296
+ if let frame { fields["frame"] = .number(Double(frame)) }
297
+ return .object(fields)
298
+ }
299
+ }
300
+
301
+ /// FNV-1a, 64 bits, as 16 hex digits: content keys for image sources.
302
+ func _fnv1a(_ bytes: [UInt8]) -> String {
303
+ var hash: UInt64 = 0xCBF2_9CE4_8422_2325
304
+ for byte in bytes {
305
+ hash ^= UInt64(byte)
306
+ hash = hash &* 0x0000_0100_0000_01B3
307
+ }
308
+ let hex = String(hash, radix: 16)
309
+ return String(repeating: "0", count: 16 - hex.count) + hex
310
+ }
@@ -8,14 +8,25 @@ public enum _JSON: Equatable, Sendable {
8
8
  case string(String)
9
9
  case array([_JSON])
10
10
  case object([String: _JSON])
11
+ /// Raw bytes: a fetched body, an image file. Serialized as a base64
12
+ /// string, except into the browser's ops buffer, where they pass through
13
+ /// linear memory as `{"$bytes": n}` (docs/ops-protocol.md, "Bytes").
14
+ case bytes([UInt8])
11
15
 
12
16
  public var serialized: String {
13
17
  var out = ""
14
- write(into: &out)
18
+ write(into: &out, outbox: nil)
15
19
  return out
16
20
  }
17
21
 
18
- private func write(into out: inout String) {
22
+ /// Serialized with `.bytes` values moved to `outbox`, each written as `{"$bytes": n}`.
23
+ func serialized(outbox: _BytesOutbox) -> String {
24
+ var out = ""
25
+ write(into: &out, outbox: outbox)
26
+ return out
27
+ }
28
+
29
+ private func write(into out: inout String, outbox: _BytesOutbox?) {
19
30
  switch self {
20
31
  case .null:
21
32
  out += "null"
@@ -35,7 +46,7 @@ public enum _JSON: Equatable, Sendable {
35
46
  out += "["
36
47
  for (index, item) in items.enumerated() {
37
48
  if index > 0 { out += "," }
38
- item.write(into: &out)
49
+ item.write(into: &out, outbox: outbox)
39
50
  }
40
51
  out += "]"
41
52
  case .object(let members):
@@ -44,9 +55,17 @@ public enum _JSON: Equatable, Sendable {
44
55
  if index > 0 { out += "," }
45
56
  _JSON.writeString(key, into: &out)
46
57
  out += ":"
47
- members[key]!.write(into: &out)
58
+ members[key]!.write(into: &out, outbox: outbox)
48
59
  }
49
60
  out += "}"
61
+ case .bytes(let bytes):
62
+ if let outbox {
63
+ out += "{\"$bytes\":\(outbox.add(bytes))}"
64
+ } else {
65
+ out += "\""
66
+ out += _base64(bytes)
67
+ out += "\""
68
+ }
50
69
  }
51
70
  }
52
71
 
@@ -69,3 +88,56 @@ public enum _JSON: Equatable, Sendable {
69
88
  out += "\""
70
89
  }
71
90
  }
91
+
92
+ /// Bytes on their way to the page beside the ops buffer (`sb_bytes_*`
93
+ /// exports): the `n` of each `{"$bytes": n}` in the ops is an index here.
94
+ final class _BytesOutbox {
95
+ private(set) var items: [UnsafeMutableBufferPointer<UInt8>] = []
96
+
97
+ func add(_ bytes: [UInt8]) -> Int {
98
+ let buffer = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: max(bytes.count, 1))
99
+ _ = buffer.initialize(from: bytes)
100
+ items.append(UnsafeMutableBufferPointer(rebasing: buffer[0..<bytes.count]))
101
+ return items.count - 1
102
+ }
103
+
104
+ func removeAll() {
105
+ for item in items { item.deallocate() }
106
+ items.removeAll()
107
+ }
108
+ }
109
+
110
+ /// Bytes the page put in linear memory for the next `sb_event_json` (the
111
+ /// `sb_bytes` export), by the `n` of the event's `{"$bytes": n}` values.
112
+ @MainActor
113
+ final class _BytesInbox {
114
+ static let shared = _BytesInbox()
115
+ private var items: [Int: [UInt8]] = [:]
116
+ private var nextID = 1
117
+
118
+ func add(_ bytes: [UInt8]) -> Int {
119
+ let id = nextID
120
+ nextID += 1
121
+ items[id] = bytes
122
+ return id
123
+ }
124
+
125
+ /// `event` with each `{"$bytes": n}` replaced by its bytes; whatever it did not name is dropped.
126
+ func resolve(_ event: _JSON) -> _JSON {
127
+ guard !items.isEmpty else { return event }
128
+ defer { items.removeAll() }
129
+ return replace(event)
130
+ }
131
+
132
+ private func replace(_ value: _JSON) -> _JSON {
133
+ switch value {
134
+ case .object(let members):
135
+ if members.count == 1, case .number(let n)? = members["$bytes"], let bytes = items[Int(n)] { return .bytes(bytes) }
136
+ return .object(members.mapValues(replace))
137
+ case .array(let items):
138
+ return .array(items.map(replace))
139
+ default:
140
+ return value
141
+ }
142
+ }
143
+ }
@@ -0,0 +1,99 @@
1
+ // Lazy rows. SwiftUI builds the rows of a `List` (or `Form`), and of a `LazyVStack` or
2
+ // `LazyHStack`, as they scroll into view: a timeline of a thousand posts
3
+ // mounts the few on screen. Building every row up front made long lists
4
+ // expensive in the browser, in time (each row's body, its elements, the
5
+ // page's layout of them) and in memory, which a phone's browser runs out of.
6
+ //
7
+ // Here a keyed group (`ForEach`, or a modifier spread over one) whose rows
8
+ // are a lazy container's rows mounts its first rows only, followed by a
9
+ // `lazyspacer` element standing for the rest. When the page sees the spacer
10
+ // come near its scroll view's visible area it sends a `lazy` event, and the
11
+ // group mounts the next rows (docs/ops-protocol.md, "Lazy rows"). Rows that
12
+ // were built stay built, as on iOS a row's state survives scrolling away.
13
+ //
14
+ // The rows of a lazy container are its content seen through groups,
15
+ // sections and views with a body of their own: `List { Section {
16
+ // ForEach(...) } }` and `List { TimelineRows() }` are lazy, while a
17
+ // `ForEach` inside a row's own `VStack` is part of that row and is built
18
+ // with it.
19
+
20
+ /// `List`, `Form`, `LazyVStack` and `LazyHStack`: their rows are built lazily.
21
+ protocol _LazyRowsContainer: _TraitBearing {
22
+ var _lazyAxis: Axis { get }
23
+ }
24
+
25
+ enum _LazyRows {
26
+ /// Rows a group mounts before its spacer, and the rows each `lazy` event adds.
27
+ static let batch = 12
28
+ }
29
+
30
+ /// The key of a group's spacer among its rows' keys. Each batch's spacer is a
31
+ /// new element (keyed by the rows before it), so the page watches it afresh:
32
+ /// one still in view after a batch was built asks for the next.
33
+ struct _LazySpacerKey: Hashable {
34
+ let built: Int
35
+ }
36
+
37
+ /// The rows of a lazy group that are not built yet (`lazyspacer` element).
38
+ struct _LazyRowsSpacer {
39
+ let axis: Axis
40
+ let remaining: Int
41
+ let extend: @MainActor () -> Void
42
+ }
43
+ extension _LazyRowsSpacer: _PrimitiveView {}
44
+
45
+ extension _LazyRowsSpacer: _HostView {
46
+ var _kind: String { "lazyspacer" }
47
+ func _props(in environment: EnvironmentValues) -> _JSON {
48
+ .object([
49
+ "axis": .string(axis == .horizontal ? "horizontal" : "vertical"),
50
+ "remaining": .number(Double(remaining)),
51
+ ])
52
+ }
53
+ var _handler: ((_JSON) -> Void)? {
54
+ let extend = extend
55
+ return { _ in MainActor.assumeIsolated { extend() } }
56
+ }
57
+ }
58
+
59
+ extension List: _LazyRowsContainer {
60
+ var _lazyAxis: Axis { .vertical }
61
+ }
62
+ /// A `Form` is a grouped list, on iOS as here.
63
+ extension Form: _LazyRowsContainer {
64
+ var _lazyAxis: Axis { .vertical }
65
+ }
66
+ extension LazyVStack: _LazyRowsContainer {
67
+ var _lazyAxis: Axis { .vertical }
68
+ }
69
+ extension LazyHStack: _LazyRowsContainer {
70
+ var _lazyAxis: Axis { .horizontal }
71
+ }
72
+
73
+ // MARK: - Lazy menus
74
+
75
+ // A `Menu`'s items and a `.contextMenu`'s are built when the menu opens, as
76
+ // on iOS, and dropped when it closes. Until then the `menu` / `contextmenu`
77
+ // element has its label (or content) only and `"deferred": true`; the page
78
+ // sends `{"type": "menu", "id": n, "open": true}` when the viewer opens it,
79
+ // shows the popover once the items arrive, and sends `"open": false` when it
80
+ // closes (docs/ops-protocol.md, "Lazy menus"). A timeline's rows each carry
81
+ // a menu or two: built eagerly they were half of Ice Cubes' page. Headless
82
+ // runs, whose event scripts tap menu items directly, build them eagerly.
83
+
84
+ /// `Menu` and `.contextMenu`: hosts whose items are built only while open.
85
+ protocol _LazyMenuHost: _TraitBearing {
86
+ /// The label (a `Menu`'s, in its `ZStack`) or the content (a context menu's).
87
+ var _menuBase: any View { get }
88
+ var _menuItems: any View { get }
89
+ }
90
+
91
+ extension Menu: _LazyMenuHost {
92
+ var _menuBase: any View { ZStack { label } }
93
+ var _menuItems: any View { content }
94
+ }
95
+
96
+ extension _ContextMenuView: _LazyMenuHost {
97
+ var _menuBase: any View { content }
98
+ var _menuItems: any View { items }
99
+ }