@swiftbrowser/swift 0.0.0-stage → 0.2.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 (400) hide show
  1. package/Package.swift +214 -0
  2. package/README.md +10 -2
  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 +61 -0
  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/FoundationBridge/Bundle.swift +119 -0
  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 +29 -0
  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/Locale.swift +86 -0
  170. package/Sources/FoundationBridge/Localization.swift +149 -0
  171. package/Sources/FoundationBridge/Locks.swift +50 -0
  172. package/Sources/FoundationBridge/Markdown.swift +527 -0
  173. package/Sources/FoundationBridge/MeasurementFormat.swift +95 -0
  174. package/Sources/FoundationBridge/NotificationCenter.swift +154 -0
  175. package/Sources/FoundationBridge/OpenURLHandlers.swift +60 -0
  176. package/Sources/FoundationBridge/Operation.swift +195 -0
  177. package/Sources/FoundationBridge/ProcessInfo.swift +32 -0
  178. package/Sources/FoundationBridge/RelativeDateTimeFormatter.swift +136 -0
  179. package/Sources/FoundationBridge/SceneStorage.swift +78 -0
  180. package/Sources/FoundationBridge/SecurityExports.swift +8 -0
  181. package/Sources/FoundationBridge/Selector.swift +226 -0
  182. package/Sources/FoundationBridge/StringComparators.swift +17 -0
  183. package/Sources/FoundationBridge/Table.swift +211 -0
  184. package/Sources/FoundationBridge/TextAttributes.swift +42 -0
  185. package/Sources/FoundationBridge/Timer.swift +53 -0
  186. package/Sources/FoundationBridge/UITextChecker.swift +30 -0
  187. package/Sources/FoundationBridge/URLSession.swift +718 -0
  188. package/Sources/FoundationBridge/UserDefaults.swift +183 -0
  189. package/Sources/FoundationBridge/WebAuthRedirects.swift +227 -0
  190. package/Sources/FoundationBridge/WebSocket.swift +187 -0
  191. package/Sources/FoundationModels/FoundationModels.swift +1098 -0
  192. package/Sources/ImageIO/GIF.swift +72 -0
  193. package/Sources/ImageIO/ImageIO.swift +248 -0
  194. package/Sources/ImageIO/ImageTypes.swift +28 -0
  195. package/Sources/LinkPresentation/LinkPresentation.swift +249 -0
  196. package/Sources/LocalAuthentication/LAContext.swift +86 -0
  197. package/Sources/MapKit/Geometry.swift +223 -0
  198. package/Sources/MapKit/Map.swift +638 -0
  199. package/Sources/MapKit/MapView.swift +229 -0
  200. package/Sources/MobileCoreServices/MobileCoreServices.swift +28 -0
  201. package/Sources/NaturalLanguage/LanguageIdentifier.swift +317 -0
  202. package/Sources/NaturalLanguage/NaturalLanguage.swift +135 -0
  203. package/Sources/OSLog/OSLog.swift +6 -0
  204. package/Sources/Photos/Photos.swift +572 -0
  205. package/Sources/PhotosUI/PhotosPicker.swift +248 -0
  206. package/Sources/PhotosUI/PhotosPickerItem.swift +261 -0
  207. package/Sources/QuickLook/QuickLook.swift +168 -0
  208. package/Sources/SafariServices/SafariServices.swift +106 -0
  209. package/Sources/Security/Security.swift +332 -0
  210. package/Sources/StoreKit/AppStore.swift +207 -0
  211. package/Sources/StoreKit/Products.swift +280 -0
  212. package/Sources/StoreKit/RequestReview.swift +28 -0
  213. package/Sources/StoreKit/StoreKit1.swift +486 -0
  214. package/Sources/StoreKit/StoreViews.swift +428 -0
  215. package/Sources/StoreKit/Transactions.swift +197 -0
  216. package/Sources/SwiftData/Macros.swift +58 -0
  217. package/Sources/SwiftData/ModelActor.swift +38 -0
  218. package/Sources/SwiftData/ModelContainer.swift +129 -0
  219. package/Sources/SwiftData/ModelContext.swift +451 -0
  220. package/Sources/SwiftData/PersistentModel.swift +238 -0
  221. package/Sources/SwiftData/Properties.swift +261 -0
  222. package/Sources/SwiftData/Query.swift +256 -0
  223. package/Sources/SwiftData/Schema.swift +192 -0
  224. package/Sources/SwiftData/Store.swift +146 -0
  225. package/Sources/SwiftData/StringPredicates.swift +125 -0
  226. package/Sources/SwiftDataMacros/EntryMacro.swift +82 -0
  227. package/Sources/SwiftDataMacros/GenerableMacro.swift +183 -0
  228. package/Sources/SwiftDataMacros/ModelActorMacro.swift +46 -0
  229. package/Sources/SwiftDataMacros/ModelMacro.swift +260 -0
  230. package/Sources/SwiftDataMacros/Plugin.swift +18 -0
  231. package/Sources/SwiftDataMacros/PredicateMacro.swift +1244 -0
  232. package/Sources/SwiftUI/API/Accessibility.swift +181 -0
  233. package/Sources/SwiftUI/API/AccessibilityExtras.swift +33 -0
  234. package/Sources/SwiftUI/API/AccessibilityFocus.swift +63 -0
  235. package/Sources/SwiftUI/API/Alert.swift +221 -0
  236. package/Sources/SwiftUI/API/AlignmentGuides.swift +65 -0
  237. package/Sources/SwiftUI/API/Animation.swift +285 -0
  238. package/Sources/SwiftUI/API/App.swift +87 -0
  239. package/Sources/SwiftUI/API/BindingCollections.swift +35 -0
  240. package/Sources/SwiftUI/API/BindingForEach.swift +27 -0
  241. package/Sources/SwiftUI/API/Bitmaps.swift +254 -0
  242. package/Sources/SwiftUI/API/ButtonStyles.swift +96 -0
  243. package/Sources/SwiftUI/API/Canvas.swift +337 -0
  244. package/Sources/SwiftUI/API/Color.swift +237 -0
  245. package/Sources/SwiftUI/API/ColorEffects.swift +52 -0
  246. package/Sources/SwiftUI/API/ColorPickerAndLabeledContent.swift +138 -0
  247. package/Sources/SwiftUI/API/ColorSpaces.swift +161 -0
  248. package/Sources/SwiftUI/API/Commands.swift +106 -0
  249. package/Sources/SwiftUI/API/ContainerBackground.swift +28 -0
  250. package/Sources/SwiftUI/API/ContentShape.swift +33 -0
  251. package/Sources/SwiftUI/API/ContentSizeCategory.swift +35 -0
  252. package/Sources/SwiftUI/API/ContentUnavailable.swift +101 -0
  253. package/Sources/SwiftUI/API/Controls.swift +300 -0
  254. package/Sources/SwiftUI/API/CustomLayout.swift +442 -0
  255. package/Sources/SwiftUI/API/CustomToolbarContent.swift +46 -0
  256. package/Sources/SwiftUI/API/Decorations.swift +92 -0
  257. package/Sources/SwiftUI/API/EditMode.swift +212 -0
  258. package/Sources/SwiftUI/API/Effects.swift +388 -0
  259. package/Sources/SwiftUI/API/Entry.swift +8 -0
  260. package/Sources/SwiftUI/API/Environment.swift +439 -0
  261. package/Sources/SwiftUI/API/EnvironmentSendable.swift +6 -0
  262. package/Sources/SwiftUI/API/Focus.swift +137 -0
  263. package/Sources/SwiftUI/API/Font.swift +131 -0
  264. package/Sources/SwiftUI/API/Gauge.swift +290 -0
  265. package/Sources/SwiftUI/API/Geometry.swift +89 -0
  266. package/Sources/SwiftUI/API/GeometryEffects.swift +232 -0
  267. package/Sources/SwiftUI/API/GeometryReader.swift +135 -0
  268. package/Sources/SwiftUI/API/GestureState.swift +65 -0
  269. package/Sources/SwiftUI/API/Gestures.swift +342 -0
  270. package/Sources/SwiftUI/API/Glass.swift +127 -0
  271. package/Sources/SwiftUI/API/Grid.swift +188 -0
  272. package/Sources/SwiftUI/API/GroupBoxAndControlGroup.swift +130 -0
  273. package/Sources/SwiftUI/API/KeyPress.swift +54 -0
  274. package/Sources/SwiftUI/API/LabelStyle.swift +124 -0
  275. package/Sources/SwiftUI/API/Layout.swift +220 -0
  276. package/Sources/SwiftUI/API/LazyStacks.swift +49 -0
  277. package/Sources/SwiftUI/API/LegacyAlert.swift +97 -0
  278. package/Sources/SwiftUI/API/Lists.swift +331 -0
  279. package/Sources/SwiftUI/API/Menu.swift +58 -0
  280. package/Sources/SwiftUI/API/Modifiers.swift +313 -0
  281. package/Sources/SwiftUI/API/MoreModifiers.swift +100 -0
  282. package/Sources/SwiftUI/API/Namespace.swift +44 -0
  283. package/Sources/SwiftUI/API/Navigation.swift +386 -0
  284. package/Sources/SwiftUI/API/NavigationDestinationBindings.swift +55 -0
  285. package/Sources/SwiftUI/API/ObservableObject.swift +293 -0
  286. package/Sources/SwiftUI/API/OnChange.swift +48 -0
  287. package/Sources/SwiftUI/API/Path.swift +543 -0
  288. package/Sources/SwiftUI/API/Phase8Controls.swift +411 -0
  289. package/Sources/SwiftUI/API/PhaseAnimator.swift +130 -0
  290. package/Sources/SwiftUI/API/Picker.swift +185 -0
  291. package/Sources/SwiftUI/API/PointerAndKeyboard.swift +77 -0
  292. package/Sources/SwiftUI/API/Popover.swift +41 -0
  293. package/Sources/SwiftUI/API/Preferences.swift +262 -0
  294. package/Sources/SwiftUI/API/Presentation.swift +122 -0
  295. package/Sources/SwiftUI/API/PresentationExtras.swift +60 -0
  296. package/Sources/SwiftUI/API/Primitives.swift +517 -0
  297. package/Sources/SwiftUI/API/Publishers.swift +107 -0
  298. package/Sources/SwiftUI/API/Redaction.swift +75 -0
  299. package/Sources/SwiftUI/API/RowActions.swift +84 -0
  300. package/Sources/SwiftUI/API/Scenes.swift +131 -0
  301. package/Sources/SwiftUI/API/ScrollModifiers.swift +132 -0
  302. package/Sources/SwiftUI/API/ScrollTargets.swift +104 -0
  303. package/Sources/SwiftUI/API/ScrollViewReader.swift +144 -0
  304. package/Sources/SwiftUI/API/SectionSupplements.swift +96 -0
  305. package/Sources/SwiftUI/API/Shape.swift +214 -0
  306. package/Sources/SwiftUI/API/ShapeStyle.swift +409 -0
  307. package/Sources/SwiftUI/API/ShapeTrim.swift +78 -0
  308. package/Sources/SwiftUI/API/Sharing.swift +193 -0
  309. package/Sources/SwiftUI/API/StackLayouts.swift +199 -0
  310. package/Sources/SwiftUI/API/State.swift +144 -0
  311. package/Sources/SwiftUI/API/StatusGaps.swift +121 -0
  312. package/Sources/SwiftUI/API/StrokeStyle.swift +30 -0
  313. package/Sources/SwiftUI/API/SymbolEffects.swift +203 -0
  314. package/Sources/SwiftUI/API/TabContent.swift +841 -0
  315. package/Sources/SwiftUI/API/Table.swift +382 -0
  316. package/Sources/SwiftUI/API/Tabs.swift +263 -0
  317. package/Sources/SwiftUI/API/Tasks.swift +35 -0
  318. package/Sources/SwiftUI/API/Text.swift +386 -0
  319. package/Sources/SwiftUI/API/TextDates.swift +61 -0
  320. package/Sources/SwiftUI/API/TextDecorations.swift +52 -0
  321. package/Sources/SwiftUI/API/TextFieldAxis.swift +103 -0
  322. package/Sources/SwiftUI/API/TextFieldCommit.swift +52 -0
  323. package/Sources/SwiftUI/API/Timeline.swift +212 -0
  324. package/Sources/SwiftUI/API/Toolbar.swift +251 -0
  325. package/Sources/SwiftUI/API/Transfer.swift +145 -0
  326. package/Sources/SwiftUI/API/UnevenRoundedRectangle.swift +66 -0
  327. package/Sources/SwiftUI/API/Video.swift +64 -0
  328. package/Sources/SwiftUI/API/View.swift +215 -0
  329. package/Sources/SwiftUI/API/ViewModifier.swift +43 -0
  330. package/Sources/SwiftUI/API/VisualEffect.swift +155 -0
  331. package/Sources/SwiftUI/API/Windows.swift +25 -0
  332. package/Sources/SwiftUI/Charts/Axis.swift +325 -0
  333. package/Sources/SwiftUI/Charts/AxisFormatsAndRanges.swift +68 -0
  334. package/Sources/SwiftUI/Charts/Chart.swift +660 -0
  335. package/Sources/SwiftUI/Charts/ChartContent.swift +376 -0
  336. package/Sources/SwiftUI/Charts/ChartProxy.swift +57 -0
  337. package/Sources/SwiftUI/Charts/Marks.swift +204 -0
  338. package/Sources/SwiftUI/Charts/Plottable.swift +99 -0
  339. package/Sources/SwiftUI/Charts/Scales.swift +326 -0
  340. package/Sources/SwiftUI/Localization/Localization.swift +498 -0
  341. package/Sources/SwiftUI/Localization/PluralRules.swift +127 -0
  342. package/Sources/SwiftUI/Runtime/Executor.swift +85 -0
  343. package/Sources/SwiftUI/Runtime/Exports.swift +101 -0
  344. package/Sources/SwiftUI/Runtime/ImageSource.swift +310 -0
  345. package/Sources/SwiftUI/Runtime/JSON.swift +143 -0
  346. package/Sources/SwiftUI/Runtime/JSONParser.swift +443 -0
  347. package/Sources/SwiftUI/Runtime/LazyRows.swift +99 -0
  348. package/Sources/SwiftUI/Runtime/Measurements.swift +140 -0
  349. package/Sources/SwiftUI/Runtime/Ops.swift +152 -0
  350. package/Sources/SwiftUI/Runtime/Reconciler.swift +219 -0
  351. package/Sources/SwiftUI/Runtime/Replay.swift +382 -0
  352. package/Sources/SwiftUI/Runtime/Runtime.swift +2146 -0
  353. package/Sources/SwiftUI/Runtime/Sameness.swift +152 -0
  354. package/Sources/SwiftUI/Runtime/ViewPath.swift +89 -0
  355. package/Sources/SwiftUI/Runtime/ViewTraits.swift +175 -0
  356. package/Sources/SwiftUI/Runtime/WebAuthHeadless.swift +49 -0
  357. package/Sources/Symbols/Symbols.swift +25 -0
  358. package/Sources/Translation/Translation.swift +423 -0
  359. package/Sources/UIKit/Activity.swift +213 -0
  360. package/Sources/UIKit/Alerts.swift +69 -0
  361. package/Sources/UIKit/AppDelegate.swift +222 -0
  362. package/Sources/UIKit/Appearance.swift +84 -0
  363. package/Sources/UIKit/ColorConversions.swift +70 -0
  364. package/Sources/UIKit/Controls.swift +225 -0
  365. package/Sources/UIKit/Device.swift +200 -0
  366. package/Sources/UIKit/Feedback.swift +65 -0
  367. package/Sources/UIKit/Font.swift +307 -0
  368. package/Sources/UIKit/Graphics.swift +204 -0
  369. package/Sources/UIKit/HostingController.swift +185 -0
  370. package/Sources/UIKit/Identity.swift +24 -0
  371. package/Sources/UIKit/Introspectable.swift +510 -0
  372. package/Sources/UIKit/Pasteboard.swift +363 -0
  373. package/Sources/UIKit/PhotoAlbum.swift +81 -0
  374. package/Sources/UIKit/RemoteNotifications.swift +15 -0
  375. package/Sources/UIKit/Representable.swift +288 -0
  376. package/Sources/UIKit/SafeArea.swift +12 -0
  377. package/Sources/UIKit/SceneDelegates.swift +132 -0
  378. package/Sources/UIKit/Scenes.swift +40 -0
  379. package/Sources/UIKit/ScrollView.swift +157 -0
  380. package/Sources/UIKit/ShortcutItems.swift +67 -0
  381. package/Sources/UIKit/SymbolConfiguration.swift +124 -0
  382. package/Sources/UIKit/TargetAction.swift +123 -0
  383. package/Sources/UIKit/TextPaste.swift +161 -0
  384. package/Sources/UIKit/TextView.swift +538 -0
  385. package/Sources/UIKit/UIKit.swift +292 -0
  386. package/Sources/UIKit/UIView.swift +855 -0
  387. package/Sources/UIKit/ViewTranslation.swift +311 -0
  388. package/Sources/UniformTypeIdentifiers/UTType.swift +196 -0
  389. package/Sources/UserNotifications/Content.swift +370 -0
  390. package/Sources/UserNotifications/NotificationCenter.swift +309 -0
  391. package/Sources/WeatherKit/WeatherKit.swift +228 -0
  392. package/Sources/WebKit/WebKit.swift +245 -0
  393. package/Sources/os/os.swift +376 -0
  394. package/Sources/zlib/zlib.swift +6 -0
  395. package/index.d.ts +2 -0
  396. package/index.mjs +16 -0
  397. package/manifest.d.mts +2 -0
  398. package/manifest.mjs +39 -0
  399. package/package.json +30 -4
  400. package/sync-sources.mjs +33 -0
@@ -0,0 +1,248 @@
1
+ // `PhotosPicker` and `.photosPicker(isPresented:)`: the page's file chooser
2
+ // in place of the photo library. A pick is a `pickFiles` host request
3
+ // (Phase 13's channel; docs/ops-protocol.md, "Photos picker") with the
4
+ // filter's `accept` and whether several files may be chosen. The page opens
5
+ // an `<input type="file">` from the tap that sent it (the request goes out
6
+ // within the same click, which a browser requires to show a chooser) and
7
+ // answers with each chosen file's bytes, type and, for a picture, an object
8
+ // URL and pixel size. A dismissed chooser answers with no files, which
9
+ // leaves the selection as it was, as on iOS.
10
+
11
+ import SwiftUI
12
+
13
+ /// One pick: what to offer, how many, and what to do with the result.
14
+ struct _PhotosPick: Sendable {
15
+ let filter: PHPickerFilter?
16
+ /// At most this many items (nil: no limit).
17
+ let limit: Int?
18
+ /// Whether the chooser lets the viewer pick several files.
19
+ let multiple: Bool
20
+ /// Whether picked items expose an `itemIdentifier` (a `photoLibrary` was given).
21
+ let exposesIdentifiers: Bool
22
+
23
+ /// The request's params: `{accept, multiple}`. No filter offers pictures and movies, the library's contents.
24
+ var params: _JSON {
25
+ .object([
26
+ "accept": .string((filter ?? .any(of: [.images, .videos])).accept),
27
+ "multiple": .bool(multiple),
28
+ ])
29
+ }
30
+
31
+ /// Asks the page and waits for the viewer. Empty when the chooser was
32
+ /// dismissed, or the page could not open one.
33
+ @MainActor func run() async -> [PhotosPickerItem] {
34
+ guard let value = try? await _Runtime.shared._hostRequest(kind: "pickFiles", params: params) else { return [] }
35
+ return items(from: value)
36
+ }
37
+
38
+ /// The items of a `pickFiles` answer (`{items: [...]}`): files the
39
+ /// filter rejects are dropped (a chooser's "All files" lets any through),
40
+ /// then the list is cut to the limit.
41
+ @MainActor func items(from value: _JSON) -> [PhotosPickerItem] {
42
+ guard case .object(let fields) = value, case .array(let entries)? = fields["items"] else { return [] }
43
+ let matching = filter ?? .any(of: [.images, .videos])
44
+ var items: [PhotosPickerItem] = []
45
+ for entry in entries {
46
+ guard let file = _PickedFile(entry), matching.matches(mimeType: file.effectiveMIMEType) else { continue }
47
+ if let limit, limit > 0, items.count >= limit { break }
48
+ _PhotosPick.picked += 1
49
+ items.append(PhotosPickerItem(file: file, key: "picked-\(_PhotosPick.picked)", exposesIdentifier: exposesIdentifiers))
50
+ }
51
+ return items
52
+ }
53
+
54
+ /// Items picked so far in this run: each new one gets the next key.
55
+ @MainActor static var picked = 0
56
+
57
+ /// Runs the pick in a task (a tap's executor run, so the request leaves
58
+ /// within the click) and hands a non-empty result to `apply`.
59
+ @MainActor func start(_ apply: @escaping @MainActor ([PhotosPickerItem]) -> Void, finished: @escaping @MainActor () -> Void = {}) {
60
+ Task { @MainActor in
61
+ let items = await run()
62
+ if !items.isEmpty { apply(items) }
63
+ finished()
64
+ }
65
+ }
66
+ }
67
+
68
+ // MARK: PhotosPicker
69
+
70
+ /// A button that opens the photo picker (the browser's file chooser) and
71
+ /// puts what the viewer picks into `selection`.
72
+ public struct PhotosPicker<Label: View>: View {
73
+ let pick: _PhotosPick
74
+ let apply: @MainActor ([PhotosPickerItem]) -> Void
75
+ let label: Label
76
+
77
+ init(pick: _PhotosPick, apply: @escaping @MainActor ([PhotosPickerItem]) -> Void, label: Label) {
78
+ self.pick = pick
79
+ self.apply = apply
80
+ self.label = label
81
+ }
82
+
83
+ public var body: some View {
84
+ let pick = pick
85
+ let apply = apply
86
+ return Button {
87
+ pick.start(apply)
88
+ } label: {
89
+ label
90
+ }
91
+ }
92
+
93
+ // One item.
94
+
95
+ public init(selection: Binding<PhotosPickerItem?>, matching filter: PHPickerFilter? = nil,
96
+ preferredItemEncoding: PhotosPickerItem.EncodingDisambiguationPolicy = .automatic,
97
+ @ViewBuilder label: () -> Label) {
98
+ self.init(pick: _PhotosPick(filter: filter, limit: 1, multiple: false, exposesIdentifiers: false),
99
+ apply: { selection.wrappedValue = $0.first }, label: label())
100
+ }
101
+
102
+ public init(selection: Binding<PhotosPickerItem?>, matching filter: PHPickerFilter? = nil,
103
+ preferredItemEncoding: PhotosPickerItem.EncodingDisambiguationPolicy = .automatic,
104
+ photoLibrary: PHPhotoLibrary, @ViewBuilder label: () -> Label) {
105
+ self.init(pick: _PhotosPick(filter: filter, limit: 1, multiple: false, exposesIdentifiers: true),
106
+ apply: { selection.wrappedValue = $0.first }, label: label())
107
+ }
108
+
109
+ // Several items.
110
+
111
+ public init(selection: Binding<[PhotosPickerItem]>, maxSelectionCount: Int? = nil,
112
+ selectionBehavior: PhotosPickerSelectionBehavior = .default, matching filter: PHPickerFilter? = nil,
113
+ preferredItemEncoding: PhotosPickerItem.EncodingDisambiguationPolicy = .automatic,
114
+ @ViewBuilder label: () -> Label) {
115
+ self.init(pick: _PhotosPick(filter: filter, limit: maxSelectionCount, multiple: maxSelectionCount != 1, exposesIdentifiers: false),
116
+ apply: { selection.wrappedValue = $0 }, label: label())
117
+ }
118
+
119
+ public init(selection: Binding<[PhotosPickerItem]>, maxSelectionCount: Int? = nil,
120
+ selectionBehavior: PhotosPickerSelectionBehavior = .default, matching filter: PHPickerFilter? = nil,
121
+ preferredItemEncoding: PhotosPickerItem.EncodingDisambiguationPolicy = .automatic,
122
+ photoLibrary: PHPhotoLibrary, @ViewBuilder label: () -> Label) {
123
+ self.init(pick: _PhotosPick(filter: filter, limit: maxSelectionCount, multiple: maxSelectionCount != 1, exposesIdentifiers: true),
124
+ apply: { selection.wrappedValue = $0 }, label: label())
125
+ }
126
+ }
127
+
128
+ extension PhotosPicker where Label == Text {
129
+ // One item, with a title.
130
+
131
+ public init(_ titleKey: LocalizedStringKey, selection: Binding<PhotosPickerItem?>, matching filter: PHPickerFilter? = nil,
132
+ preferredItemEncoding: PhotosPickerItem.EncodingDisambiguationPolicy = .automatic) {
133
+ self.init(selection: selection, matching: filter, preferredItemEncoding: preferredItemEncoding) { Text(titleKey) }
134
+ }
135
+
136
+ @_disfavoredOverload
137
+ public init<S: StringProtocol>(_ title: S, selection: Binding<PhotosPickerItem?>, matching filter: PHPickerFilter? = nil,
138
+ preferredItemEncoding: PhotosPickerItem.EncodingDisambiguationPolicy = .automatic) {
139
+ self.init(selection: selection, matching: filter, preferredItemEncoding: preferredItemEncoding) { Text(title) }
140
+ }
141
+
142
+ public init(_ titleKey: LocalizedStringKey, selection: Binding<PhotosPickerItem?>, matching filter: PHPickerFilter? = nil,
143
+ preferredItemEncoding: PhotosPickerItem.EncodingDisambiguationPolicy = .automatic, photoLibrary: PHPhotoLibrary) {
144
+ self.init(selection: selection, matching: filter, preferredItemEncoding: preferredItemEncoding, photoLibrary: photoLibrary) { Text(titleKey) }
145
+ }
146
+
147
+ @_disfavoredOverload
148
+ public init<S: StringProtocol>(_ title: S, selection: Binding<PhotosPickerItem?>, matching filter: PHPickerFilter? = nil,
149
+ preferredItemEncoding: PhotosPickerItem.EncodingDisambiguationPolicy = .automatic,
150
+ photoLibrary: PHPhotoLibrary) {
151
+ self.init(selection: selection, matching: filter, preferredItemEncoding: preferredItemEncoding, photoLibrary: photoLibrary) { Text(title) }
152
+ }
153
+
154
+ // Several items, with a title.
155
+
156
+ public init(_ titleKey: LocalizedStringKey, selection: Binding<[PhotosPickerItem]>, maxSelectionCount: Int? = nil,
157
+ selectionBehavior: PhotosPickerSelectionBehavior = .default, matching filter: PHPickerFilter? = nil,
158
+ preferredItemEncoding: PhotosPickerItem.EncodingDisambiguationPolicy = .automatic) {
159
+ self.init(selection: selection, maxSelectionCount: maxSelectionCount, selectionBehavior: selectionBehavior,
160
+ matching: filter, preferredItemEncoding: preferredItemEncoding) { Text(titleKey) }
161
+ }
162
+
163
+ @_disfavoredOverload
164
+ public init<S: StringProtocol>(_ title: S, selection: Binding<[PhotosPickerItem]>, maxSelectionCount: Int? = nil,
165
+ selectionBehavior: PhotosPickerSelectionBehavior = .default, matching filter: PHPickerFilter? = nil,
166
+ preferredItemEncoding: PhotosPickerItem.EncodingDisambiguationPolicy = .automatic) {
167
+ self.init(selection: selection, maxSelectionCount: maxSelectionCount, selectionBehavior: selectionBehavior,
168
+ matching: filter, preferredItemEncoding: preferredItemEncoding) { Text(title) }
169
+ }
170
+
171
+ public init(_ titleKey: LocalizedStringKey, selection: Binding<[PhotosPickerItem]>, maxSelectionCount: Int? = nil,
172
+ selectionBehavior: PhotosPickerSelectionBehavior = .default, matching filter: PHPickerFilter? = nil,
173
+ preferredItemEncoding: PhotosPickerItem.EncodingDisambiguationPolicy = .automatic, photoLibrary: PHPhotoLibrary) {
174
+ self.init(selection: selection, maxSelectionCount: maxSelectionCount, selectionBehavior: selectionBehavior,
175
+ matching: filter, preferredItemEncoding: preferredItemEncoding, photoLibrary: photoLibrary) { Text(titleKey) }
176
+ }
177
+
178
+ @_disfavoredOverload
179
+ public init<S: StringProtocol>(_ title: S, selection: Binding<[PhotosPickerItem]>, maxSelectionCount: Int? = nil,
180
+ selectionBehavior: PhotosPickerSelectionBehavior = .default, matching filter: PHPickerFilter? = nil,
181
+ preferredItemEncoding: PhotosPickerItem.EncodingDisambiguationPolicy = .automatic,
182
+ photoLibrary: PHPhotoLibrary) {
183
+ self.init(selection: selection, maxSelectionCount: maxSelectionCount, selectionBehavior: selectionBehavior,
184
+ matching: filter, preferredItemEncoding: preferredItemEncoding, photoLibrary: photoLibrary) { Text(title) }
185
+ }
186
+ }
187
+
188
+ // MARK: .photosPicker(isPresented:)
189
+
190
+ /// Opens the picker when `isPresented` becomes true (or is true when the
191
+ /// view appears) and sets it back to false once the viewer is done. Opening
192
+ /// the chooser needs a tap, so set `isPresented` from a button's action.
193
+ struct _PhotosPickerPresenter<Content: View>: View {
194
+ let content: Content
195
+ let isPresented: Binding<Bool>
196
+ let pick: _PhotosPick
197
+ let apply: @MainActor ([PhotosPickerItem]) -> Void
198
+
199
+ var body: some View {
200
+ let isPresented = isPresented
201
+ let pick = pick
202
+ let apply = apply
203
+ return content.onChange(of: isPresented.wrappedValue, initial: true) { _, presented in
204
+ guard presented else { return }
205
+ MainActor.assumeIsolated {
206
+ pick.start(apply) { isPresented.wrappedValue = false }
207
+ }
208
+ }
209
+ }
210
+ }
211
+
212
+ extension View {
213
+ public func photosPicker(isPresented: Binding<Bool>, selection: Binding<PhotosPickerItem?>,
214
+ matching filter: PHPickerFilter? = nil,
215
+ preferredItemEncoding: PhotosPickerItem.EncodingDisambiguationPolicy = .automatic) -> some View {
216
+ _PhotosPickerPresenter(content: self, isPresented: isPresented,
217
+ pick: _PhotosPick(filter: filter, limit: 1, multiple: false, exposesIdentifiers: false),
218
+ apply: { selection.wrappedValue = $0.first })
219
+ }
220
+
221
+ public func photosPicker(isPresented: Binding<Bool>, selection: Binding<PhotosPickerItem?>,
222
+ matching filter: PHPickerFilter? = nil,
223
+ preferredItemEncoding: PhotosPickerItem.EncodingDisambiguationPolicy = .automatic,
224
+ photoLibrary: PHPhotoLibrary) -> some View {
225
+ _PhotosPickerPresenter(content: self, isPresented: isPresented,
226
+ pick: _PhotosPick(filter: filter, limit: 1, multiple: false, exposesIdentifiers: true),
227
+ apply: { selection.wrappedValue = $0.first })
228
+ }
229
+
230
+ public func photosPicker(isPresented: Binding<Bool>, selection: Binding<[PhotosPickerItem]>,
231
+ maxSelectionCount: Int? = nil, selectionBehavior: PhotosPickerSelectionBehavior = .default,
232
+ matching filter: PHPickerFilter? = nil,
233
+ preferredItemEncoding: PhotosPickerItem.EncodingDisambiguationPolicy = .automatic) -> some View {
234
+ _PhotosPickerPresenter(content: self, isPresented: isPresented,
235
+ pick: _PhotosPick(filter: filter, limit: maxSelectionCount, multiple: maxSelectionCount != 1, exposesIdentifiers: false),
236
+ apply: { selection.wrappedValue = $0 })
237
+ }
238
+
239
+ public func photosPicker(isPresented: Binding<Bool>, selection: Binding<[PhotosPickerItem]>,
240
+ maxSelectionCount: Int? = nil, selectionBehavior: PhotosPickerSelectionBehavior = .default,
241
+ matching filter: PHPickerFilter? = nil,
242
+ preferredItemEncoding: PhotosPickerItem.EncodingDisambiguationPolicy = .automatic,
243
+ photoLibrary: PHPhotoLibrary) -> some View {
244
+ _PhotosPickerPresenter(content: self, isPresented: isPresented,
245
+ pick: _PhotosPick(filter: filter, limit: maxSelectionCount, multiple: maxSelectionCount != 1, exposesIdentifiers: true),
246
+ apply: { selection.wrappedValue = $0 })
247
+ }
248
+ }
@@ -0,0 +1,261 @@
1
+ // PhotosUI's model types: the picked item, the filter and the library. The
2
+ // browser has no photo library; the page's file chooser stands in for it
3
+ // (docs/ops-protocol.md, "Photos picker"). A pick delivers every chosen
4
+ // file's bytes with the selection, so `loadTransferable` answers at once.
5
+
6
+ import Foundation
7
+ import FoundationBridge
8
+ // As on iOS, PhotosUI brings Photos along (`PHPhotoLibrary`, `PHAsset`).
9
+ @_exported import Photos
10
+ import SwiftUI
11
+ import UniformTypeIdentifiers
12
+
13
+ /// One picked photo or video. Two items are equal when they come from the
14
+ /// same pick of the same file; picking the same file again makes a new item,
15
+ /// so `onChange(of:)` sees every pick.
16
+ public struct PhotosPickerItem: Equatable, Hashable, Sendable {
17
+ /// How the picker hands over a photo with several encodings. Accepted for
18
+ /// source compatibility: the browser delivers the file as it is.
19
+ public enum EncodingDisambiguationPolicy: Equatable, Hashable, Sendable {
20
+ case automatic
21
+ case current
22
+ case compatible
23
+ }
24
+
25
+ /// The asset's identifier in the photo library: like iOS, nil unless the
26
+ /// picker was given a `photoLibrary`.
27
+ public let itemIdentifier: String?
28
+ /// The file's type (from its MIME type, else its extension).
29
+ public let supportedContentTypes: [UTType]
30
+
31
+ /// Identity: unique per picked file per pick.
32
+ let key: String
33
+ let file: _PickedFile?
34
+
35
+ /// An item for an asset you already know the identifier of. The browser
36
+ /// has no library to load it from: `loadTransferable` returns nil.
37
+ public init(itemIdentifier: String) {
38
+ self.itemIdentifier = itemIdentifier
39
+ supportedContentTypes = []
40
+ key = "id:\(itemIdentifier)"
41
+ file = nil
42
+ }
43
+
44
+ init(file: _PickedFile, key: String, exposesIdentifier: Bool) {
45
+ itemIdentifier = exposesIdentifier ? key : nil
46
+ supportedContentTypes = file.contentType.map { [$0] } ?? []
47
+ self.key = key
48
+ self.file = file
49
+ }
50
+
51
+ public static func == (lhs: PhotosPickerItem, rhs: PhotosPickerItem) -> Bool { lhs.key == rhs.key }
52
+ public func hash(into hasher: inout Hasher) { hasher.combine(key) }
53
+
54
+ /// The item as `type`. `Data` is the file's bytes; `Image` shows the
55
+ /// picture (a still image the browser could decode). Any other type
56
+ /// imports through its first representation that takes the file's type
57
+ /// (a `FileRepresentation(importedContentType: .image)` receives a copy
58
+ /// of the file); nil when none does.
59
+ nonisolated(nonsending) public func loadTransferable<T: Transferable>(type: T.Type) async throws -> T? {
60
+ if let known = try loadKnown(type) { return known }
61
+ return try await loadImported(type)
62
+ }
63
+
64
+ /// The completion-handler form. For `Data` and `Image` the handler runs
65
+ /// before this returns; other types load in a task.
66
+ @discardableResult
67
+ public func loadTransferable<T: Transferable>(type: T.Type,
68
+ completionHandler: @escaping @Sendable (Result<T?, any Error>) -> Void) -> Progress {
69
+ let progress = Progress(totalUnitCount: 1)
70
+ do {
71
+ if let known = try loadKnown(type) {
72
+ completionHandler(.success(known))
73
+ progress.completedUnitCount = 1
74
+ return progress
75
+ }
76
+ } catch {
77
+ completionHandler(.failure(error))
78
+ progress.completedUnitCount = 1
79
+ return progress
80
+ }
81
+ let item = self
82
+ let job = _PhotosLoadJob(progress: progress, work: { try await item.loadImported(type) },
83
+ done: { result in completionHandler(result.map { $0 as? T }) })
84
+ job.start()
85
+ return progress
86
+ }
87
+
88
+ /// `Data` and `Image`, answered from the pick at once: `.some(value)`;
89
+ /// nil for the other types.
90
+ private func loadKnown<T: Transferable>(_ type: T.Type) throws -> T?? {
91
+ guard T.self == Data.self || T.self == Image.self else { return nil }
92
+ guard let file else { return .some(nil) }
93
+ if T.self == Data.self {
94
+ guard let data = Data(base64Encoded: file.base64) else {
95
+ throw CocoaError(.fileReadCorruptFile)
96
+ }
97
+ return .some(data as? T)
98
+ }
99
+ guard let url = file.url, let width = file.pixelWidth, let height = file.pixelHeight else { return .some(nil) }
100
+ return .some(Image(_url: url, pixelWidth: width, pixelHeight: height, scale: 1) as? T)
101
+ }
102
+
103
+ /// Any other type, through its importing representations.
104
+ nonisolated(nonsending) func loadImported<T: Transferable>(_ type: T.Type) async throws -> T? {
105
+ guard let file, let contentType = file.contentType else { return nil }
106
+ guard _Transfer.canImport(T.self, contentType: contentType) else { return nil }
107
+ guard let data = Data(base64Encoded: file.base64) else { throw CocoaError(.fileReadCorruptFile) }
108
+ let name = file.name.isEmpty ? nil : (file.name as NSString).deletingPathExtension
109
+ return try await _Transfer.import(T.self, from: _TransferContent(contentType: contentType, data: data, suggestedName: name))
110
+ }
111
+ }
112
+
113
+ /// One completion-handler load of a type that imports through its
114
+ /// representations (type-erased, so the task carries no generic context).
115
+ final class _PhotosLoadJob: @unchecked Sendable {
116
+ let work: () async throws -> Any?
117
+ let done: (Result<Any?, any Error>) -> Void
118
+ let progress: Progress
119
+
120
+ init(progress: Progress, work: @escaping () async throws -> Any?, done: @escaping (Result<Any?, any Error>) -> Void) {
121
+ self.work = work
122
+ self.done = done
123
+ self.progress = progress
124
+ }
125
+
126
+ func start() {
127
+ Task { await self.run() }
128
+ }
129
+
130
+ func run() async {
131
+ var result: Result<Any?, any Error>
132
+ do {
133
+ let value = try await work()
134
+ result = .success(value)
135
+ } catch {
136
+ result = .failure(error)
137
+ }
138
+ progress.completedUnitCount = 1
139
+ done(result)
140
+ }
141
+ }
142
+
143
+ /// A file the page picked: its bytes (base64), type and, for a picture the
144
+ /// browser could decode, an object URL it can show and its pixel size.
145
+ struct _PickedFile: Sendable {
146
+ let name: String
147
+ let mimeType: String
148
+ let base64: String
149
+ let url: String?
150
+ let pixelWidth: Double?
151
+ let pixelHeight: Double?
152
+
153
+ /// From one entry of a `pickFiles` answer: `{name, type, data, url?, width?, height?}`.
154
+ init?(_ json: _JSON) {
155
+ guard case .object(let fields) = json, case .string(let data)? = fields["data"] else { return nil }
156
+ if case .string(let name)? = fields["name"] { self.name = name } else { name = "" }
157
+ if case .string(let type)? = fields["type"] { mimeType = type.lowercased() } else { mimeType = "" }
158
+ base64 = data
159
+ if case .string(let url)? = fields["url"] { self.url = url } else { url = nil }
160
+ if case .number(let width)? = fields["width"], case .number(let height)? = fields["height"], width > 0, height > 0 {
161
+ pixelWidth = width
162
+ pixelHeight = height
163
+ } else {
164
+ pixelWidth = nil
165
+ pixelHeight = nil
166
+ }
167
+ }
168
+
169
+ /// The file's type: its MIME type when the browser knows it, else its extension.
170
+ var contentType: UTType? {
171
+ if !mimeType.isEmpty, let type = UTType(mimeType: mimeType) { return type }
172
+ guard let dot = name.lastIndex(of: "."), dot != name.index(before: name.endIndex) else { return nil }
173
+ return UTType(filenameExtension: String(name[name.index(after: dot)...]))
174
+ }
175
+
176
+ /// The MIME type the filter is matched against (from the extension when the browser reported none).
177
+ var effectiveMIMEType: String {
178
+ mimeType.isEmpty ? (contentType?.preferredMIMEType ?? "") : mimeType
179
+ }
180
+ }
181
+
182
+
183
+ // MARK: Filter
184
+
185
+ /// Which assets the picker offers. In the browser it becomes the file
186
+ /// chooser's `accept` (`image/*`, `video/*`); the picked files are matched
187
+ /// against it again, since a chooser lets the viewer pick any file.
188
+ public struct PHPickerFilter: Hashable, Sendable {
189
+ indirect enum Rule: Hashable, Sendable {
190
+ case media(Media)
191
+ case any([PHPickerFilter])
192
+ case all([PHPickerFilter])
193
+ case not(PHPickerFilter)
194
+ }
195
+
196
+ /// What a kind of asset is made of, as far as a file's MIME type tells.
197
+ enum Media: Hashable, Sendable {
198
+ case image, video, imageOrVideo
199
+ }
200
+
201
+ let rule: Rule
202
+
203
+ public static let images = PHPickerFilter(rule: .media(.image))
204
+ public static let videos = PHPickerFilter(rule: .media(.video))
205
+ public static let livePhotos = PHPickerFilter(rule: .media(.image))
206
+ public static let depthEffectPhotos = PHPickerFilter(rule: .media(.image))
207
+ public static let panoramas = PHPickerFilter(rule: .media(.image))
208
+ public static let screenshots = PHPickerFilter(rule: .media(.image))
209
+ public static let bursts = PHPickerFilter(rule: .media(.image))
210
+ public static let screenRecordings = PHPickerFilter(rule: .media(.video))
211
+ public static let slomoVideos = PHPickerFilter(rule: .media(.video))
212
+ public static let timelapseVideos = PHPickerFilter(rule: .media(.video))
213
+ public static let cinematicVideos = PHPickerFilter(rule: .media(.video))
214
+ public static let spatialMedia = PHPickerFilter(rule: .media(.imageOrVideo))
215
+
216
+ public static func any(of subfilters: [PHPickerFilter]) -> PHPickerFilter { PHPickerFilter(rule: .any(subfilters)) }
217
+ public static func all(of subfilters: [PHPickerFilter]) -> PHPickerFilter { PHPickerFilter(rule: .all(subfilters)) }
218
+ public static func not(_ filter: PHPickerFilter) -> PHPickerFilter { PHPickerFilter(rule: .not(filter)) }
219
+
220
+ /// Whether a file of this MIME type passes. A photo library holds only
221
+ /// pictures and movies, so nothing else ever does.
222
+ func matches(mimeType: String) -> Bool {
223
+ let isImage = mimeType.hasPrefix("image/")
224
+ let isVideo = mimeType.hasPrefix("video/")
225
+ guard isImage || isVideo else { return false }
226
+ return evaluate(isImage: isImage)
227
+ }
228
+
229
+ private func evaluate(isImage: Bool) -> Bool {
230
+ switch rule {
231
+ case .media(.image): return isImage
232
+ case .media(.video): return !isImage
233
+ case .media(.imageOrVideo): return true
234
+ case .any(let filters): return filters.contains { $0.evaluate(isImage: isImage) }
235
+ case .all(let filters): return filters.allSatisfy { $0.evaluate(isImage: isImage) }
236
+ case .not(let filter): return !filter.evaluate(isImage: isImage)
237
+ }
238
+ }
239
+
240
+ /// The file chooser's `accept`: the media kinds that can pass.
241
+ var accept: String {
242
+ [(evaluate(isImage: true), "image/*"), (evaluate(isImage: false), "video/*")]
243
+ .filter(\.0).map(\.1).joined(separator: ",")
244
+ }
245
+ }
246
+
247
+ // MARK: Library
248
+
249
+ // `PHPhotoLibrary`, `PHAccessLevel` and `PHAuthorizationStatus` are Photos'
250
+ // (Sources/Photos), which PhotosUI re-exports, as on iOS.
251
+
252
+ /// How a multiple-selection picker orders and updates its selection.
253
+ /// Accepted for source compatibility: the browser's chooser returns the
254
+ /// files in its own order, once.
255
+ public struct PhotosPickerSelectionBehavior: Equatable, Hashable, Sendable {
256
+ let name: String
257
+ public static let `default` = PhotosPickerSelectionBehavior(name: "default")
258
+ public static let ordered = PhotosPickerSelectionBehavior(name: "ordered")
259
+ public static let continuous = PhotosPickerSelectionBehavior(name: "continuous")
260
+ public static let continuousAndOrdered = PhotosPickerSelectionBehavior(name: "continuousAndOrdered")
261
+ }
@@ -0,0 +1,168 @@
1
+ // `import QuickLook` for the browser: a preview is the browser's own viewer
2
+ // in a new tab. `.quickLookPreview($url)` (and `(_:in:)`, which previews the
3
+ // selection) opens the URL when the binding is set, then sets it back to nil,
4
+ // as the preview's dismissal does on iOS: a web URL opens as a link would,
5
+ // and a file of the app's (Ice Cubes saves the picture it previews into its
6
+ // caches) is handed to the page, which opens it as a blob in a new tab, or
7
+ // downloads it when the browser blocks the tab (a tab opened long after the
8
+ // tap that asked for it). Both are a `media` host request with `action:
9
+ // "open"` (docs/ops-protocol.md, "Video and audio").
10
+ //
11
+ // `QLPreviewController` does the same for its data source's current item when
12
+ // presented, then dismisses itself, like SafariServices' controller.
13
+
14
+ import Foundation
15
+ import FoundationBridge
16
+ import SwiftUI
17
+ import UIKit
18
+
19
+ /// The page's side of a preview; tests replace `open`.
20
+ @MainActor
21
+ public enum _QuickLookHost {
22
+ public static var open: @MainActor (URL) async -> Void = { url in
23
+ let params: _JSON
24
+ if url.isFileURL {
25
+ guard let data = try? Data(contentsOf: url) else { return }
26
+ params = .object([
27
+ "action": .string("open"),
28
+ "name": .string(url.lastPathComponent),
29
+ "type": .string(_mediaType(forPathExtension: url.pathExtension)),
30
+ "data": .string(_base64(Array(data))),
31
+ ])
32
+ } else {
33
+ params = .object(["action": .string("open"), "url": .string(url.absoluteString)])
34
+ }
35
+ _ = try? await _Runtime.shared._hostRequest(kind: "media", params: params)
36
+ }
37
+ }
38
+
39
+ /// The media type the page opens a file as, from its extension.
40
+ func _mediaType(forPathExtension pathExtension: String) -> String {
41
+ switch pathExtension.lowercased() {
42
+ case "jpg", "jpeg": "image/jpeg"
43
+ case "png": "image/png"
44
+ case "gif": "image/gif"
45
+ case "webp": "image/webp"
46
+ case "heic", "heif": "image/heic"
47
+ case "avif": "image/avif"
48
+ case "svg": "image/svg+xml"
49
+ case "pdf": "application/pdf"
50
+ case "mp4", "m4v": "video/mp4"
51
+ case "mov": "video/quicktime"
52
+ case "webm": "video/webm"
53
+ case "mp3": "audio/mpeg"
54
+ case "m4a": "audio/mp4"
55
+ case "wav": "audio/wav"
56
+ case "txt", "md", "swift": "text/plain"
57
+ case "html", "htm": "text/html"
58
+ case "json": "application/json"
59
+ case "csv": "text/csv"
60
+ default: "application/octet-stream"
61
+ }
62
+ }
63
+
64
+ extension View {
65
+ /// Previews the URL in a new tab when `item` is set, then sets it to nil.
66
+ public func quickLookPreview(_ item: Binding<URL?>) -> some View {
67
+ modifier(_QuickLookPreviewModifier(item: item))
68
+ }
69
+
70
+ /// Previews the selected URL (the browser shows one file at a time; the
71
+ /// other items are not offered), then sets the selection to nil.
72
+ public func quickLookPreview<Items: RandomAccessCollection>(_ selection: Binding<Items.Element?>, in items: Items) -> some View
73
+ where Items.Element == URL {
74
+ modifier(_QuickLookPreviewModifier(item: selection))
75
+ }
76
+ }
77
+
78
+ struct _QuickLookPreviewModifier: ViewModifier {
79
+ @Binding var item: URL?
80
+
81
+ func body(content: Content) -> some View {
82
+ content.onChange(of: item, initial: true) { _, url in
83
+ guard let url else { return }
84
+ item = nil
85
+ Task { @MainActor in await _QuickLookHost.open(url) }
86
+ }
87
+ }
88
+ }
89
+
90
+ // MARK: - QLPreviewController
91
+
92
+ /// Something a preview controller can show. `URL` conforms.
93
+ public protocol QLPreviewItem {
94
+ var previewItemURL: URL? { get }
95
+ var previewItemTitle: String? { get }
96
+ }
97
+
98
+ extension QLPreviewItem {
99
+ public var previewItemTitle: String? { nil }
100
+ }
101
+
102
+ extension URL: QLPreviewItem {
103
+ public var previewItemURL: URL? { self }
104
+ }
105
+
106
+ extension NSURL: QLPreviewItem {
107
+ public var previewItemURL: URL? { self as URL }
108
+ }
109
+
110
+ @MainActor
111
+ public protocol QLPreviewControllerDataSource: AnyObject {
112
+ func numberOfPreviewItems(in controller: QLPreviewController) -> Int
113
+ func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> any QLPreviewItem
114
+ }
115
+
116
+ /// What a preview controller tells its owner. Every method is optional.
117
+ @MainActor
118
+ public protocol QLPreviewControllerDelegate: AnyObject {
119
+ func previewControllerWillDismiss(_ controller: QLPreviewController)
120
+ func previewControllerDidDismiss(_ controller: QLPreviewController)
121
+ func previewController(_ controller: QLPreviewController, shouldOpen url: URL, for item: any QLPreviewItem) -> Bool
122
+ }
123
+
124
+ extension QLPreviewControllerDelegate {
125
+ public func previewControllerWillDismiss(_ controller: QLPreviewController) {}
126
+ public func previewControllerDidDismiss(_ controller: QLPreviewController) {}
127
+ public func previewController(_ controller: QLPreviewController, shouldOpen url: URL, for item: any QLPreviewItem) -> Bool { true }
128
+ }
129
+
130
+ /// Previews its data source's current item in a new tab when presented (see
131
+ /// the top of the file), then dismisses itself.
132
+ @MainActor
133
+ open class QLPreviewController: UIViewController {
134
+ open weak var dataSource: (any QLPreviewControllerDataSource)?
135
+ open weak var delegate: (any QLPreviewControllerDelegate)?
136
+ open var currentPreviewItemIndex = 0
137
+ private var opened = false
138
+
139
+ public init() {
140
+ super.init(nibName: nil, bundle: nil)
141
+ }
142
+
143
+ public required init?(coder: NSCoder) { nil }
144
+
145
+ /// Any URL the page can open, or a file it can hand over.
146
+ public static func canPreview(_ item: any QLPreviewItem) -> Bool { item.previewItemURL != nil }
147
+
148
+ open var currentPreviewItem: (any QLPreviewItem)? {
149
+ guard let dataSource, currentPreviewItemIndex >= 0,
150
+ currentPreviewItemIndex < dataSource.numberOfPreviewItems(in: self) else { return nil }
151
+ return dataSource.previewController(self, previewItemAt: currentPreviewItemIndex)
152
+ }
153
+
154
+ open func reloadData() {}
155
+ open func refreshCurrentPreviewItem() {}
156
+
157
+ open override func viewDidAppear(_ animated: Bool) {
158
+ super.viewDidAppear(animated)
159
+ guard !opened else { return }
160
+ opened = true
161
+ if let url = currentPreviewItem?.previewItemURL {
162
+ Task { @MainActor in await _QuickLookHost.open(url) }
163
+ }
164
+ delegate?.previewControllerWillDismiss(self)
165
+ dismiss(animated: animated)
166
+ delegate?.previewControllerDidDismiss(self)
167
+ }
168
+ }