@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
@@ -0,0 +1,524 @@
1
+ // Localized strings, as `Text("key")`, `String(localized:)` and
2
+ // `Bundle.localizedString(forKey:value:table:)` find them: the app's string
3
+ // tables are `<language>.lproj/<Table>.strings` and `.stringsdict` files in its
4
+ // bundle, which the CLI compiles from the Xcode target's string catalogs
5
+ // (`.xcstrings`) and classic `.strings` files, as Xcode does (packages/cli,
6
+ // strings.ts). Both are XML property lists: a `.strings` table maps keys to
7
+ // format strings, a `.stringsdict` maps keys to an `NSStringLocalizedFormatKey`
8
+ // whose `%#@name@` tokens a variable resolves (a plural rule).
9
+ //
10
+ // A key is looked up in the table of the language the app runs in (the
11
+ // viewer's, among the bundle's `.lproj` folders), then in the development
12
+ // language's; a key found in neither is its own text. Formatting follows
13
+ // `String(format:)` for the specifiers Xcode puts in keys (`%@`, `%lld`, `%lf`,
14
+ // `%d`, positional `%1$@`…), with each argument in the slot its specifier names.
15
+ // The engine lives in SwiftUI so that `Text` uses it; FoundationBridge's
16
+ // `Bundle` and `String(localized:)` call it too.
17
+
18
+ import Foundation
19
+
20
+ /// A value interpolated into a localized string: what its specifier formats.
21
+ public enum _LocalizationArgument: Hashable, Sendable, Codable {
22
+ case string(String)
23
+ case int(Int64)
24
+ case uint(UInt64)
25
+ case double(Double)
26
+
27
+ /// The number a plural rule counts (nil for a string that is no number).
28
+ var number: Double? {
29
+ switch self {
30
+ case .int(let value): Double(value)
31
+ case .uint(let value): Double(value)
32
+ case .double(let value): value
33
+ case .string(let value): Double(value)
34
+ }
35
+ }
36
+
37
+ var text: String {
38
+ switch self {
39
+ case .string(let value): value
40
+ case .int(let value): String(value)
41
+ case .uint(let value): String(value)
42
+ case .double(let value): value.rounded() == value && abs(value) < 1e15 ? String(Int64(value)) : String(value)
43
+ }
44
+ }
45
+ }
46
+
47
+ /// A number type and the specifier SwiftUI and Foundation write for it in a
48
+ /// localization key (`%lld` for `Int`): one generic interpolation takes them
49
+ /// all, as SwiftUI's does, which keeps type checking fast.
50
+ public protocol _FormatSpecifiable {
51
+ static var _specifier: String { get }
52
+ var _localizationArgument: _LocalizationArgument { get }
53
+ }
54
+
55
+ extension Int: _FormatSpecifiable {
56
+ public static var _specifier: String { "%lld" }
57
+ public var _localizationArgument: _LocalizationArgument { .int(Int64(self)) }
58
+ }
59
+ extension Int64: _FormatSpecifiable {
60
+ public static var _specifier: String { "%lld" }
61
+ public var _localizationArgument: _LocalizationArgument { .int(self) }
62
+ }
63
+ extension Int32: _FormatSpecifiable {
64
+ public static var _specifier: String { "%d" }
65
+ public var _localizationArgument: _LocalizationArgument { .int(Int64(self)) }
66
+ }
67
+ extension Int16: _FormatSpecifiable {
68
+ public static var _specifier: String { "%d" }
69
+ public var _localizationArgument: _LocalizationArgument { .int(Int64(self)) }
70
+ }
71
+ extension Int8: _FormatSpecifiable {
72
+ public static var _specifier: String { "%d" }
73
+ public var _localizationArgument: _LocalizationArgument { .int(Int64(self)) }
74
+ }
75
+ extension UInt: _FormatSpecifiable {
76
+ public static var _specifier: String { "%llu" }
77
+ public var _localizationArgument: _LocalizationArgument { .uint(UInt64(self)) }
78
+ }
79
+ extension UInt64: _FormatSpecifiable {
80
+ public static var _specifier: String { "%llu" }
81
+ public var _localizationArgument: _LocalizationArgument { .uint(self) }
82
+ }
83
+ extension UInt32: _FormatSpecifiable {
84
+ public static var _specifier: String { "%u" }
85
+ public var _localizationArgument: _LocalizationArgument { .uint(UInt64(self)) }
86
+ }
87
+ extension UInt16: _FormatSpecifiable {
88
+ public static var _specifier: String { "%u" }
89
+ public var _localizationArgument: _LocalizationArgument { .uint(UInt64(self)) }
90
+ }
91
+ extension UInt8: _FormatSpecifiable {
92
+ public static var _specifier: String { "%u" }
93
+ public var _localizationArgument: _LocalizationArgument { .uint(UInt64(self)) }
94
+ }
95
+ extension Double: _FormatSpecifiable {
96
+ public static var _specifier: String { "%lf" }
97
+ public var _localizationArgument: _LocalizationArgument { .double(self) }
98
+ }
99
+ extension Float: _FormatSpecifiable {
100
+ public static var _specifier: String { "%f" }
101
+ public var _localizationArgument: _LocalizationArgument { .double(Double(self)) }
102
+ }
103
+
104
+ /// A piece of a formatted localized string: text, or the argument (by index)
105
+ /// that a view takes the place of (an `Image` or `Text` in `Text("…\(image)")`).
106
+ public enum _LocalizedPiece: Equatable, Sendable {
107
+ case text(String)
108
+ case argument(Int)
109
+ }
110
+
111
+ /// A table entry: a format string, or a `.stringsdict` rule.
112
+ enum _LocalizedEntry: Equatable {
113
+ case format(String)
114
+ case rule(formatKey: String, variables: [String: [String: String]])
115
+ }
116
+
117
+ public enum _Localization {
118
+ /// Where the app's resources are mounted (`SB_BUNDLE_PATH`), as `Bundle.main` reads them.
119
+ public static var mainBundlePath: String { _environment("SB_BUNDLE_PATH") ?? "/bundle" }
120
+
121
+ // MARK: Languages
122
+
123
+ /// The `<language>.lproj` folders of a bundle (without `Base`), sorted;
124
+ /// read once per bundle (an app's bundle does not change while it runs).
125
+ public static func localizations(bundlePath: String) -> [String] {
126
+ cacheLock.lock()
127
+ defer { cacheLock.unlock() }
128
+ if let cached = languagesByBundle[bundlePath] { return cached }
129
+ let names = (try? FileManager.default.contentsOfDirectory(atPath: bundlePath)) ?? []
130
+ let found = names.filter { $0.hasSuffix(".lproj") }.map { String($0.dropLast(6)) }.filter { $0 != "Base" }.sorted()
131
+ languagesByBundle[bundlePath] = found
132
+ return found
133
+ }
134
+
135
+ /// The localization the app runs in: the viewer's language (`SB_LOCALE`)
136
+ /// matched against `available`, else the development localization.
137
+ public static func preferredLocalization(from available: [String]) -> String? {
138
+ preferredLocalizations(from: available, forPreferences: _environment("SB_LOCALE").map { [$0] }).first
139
+ }
140
+
141
+ /// The first of `available` that matches a preference, by whole tag
142
+ /// (`pt-BR`, case and `_` / `-` aside), then by script (`zh-Hant` for
143
+ /// `zh-TW`), then by language (`pt`); without a match, `en` when listed,
144
+ /// else the first.
145
+ public static func preferredLocalizations(from available: [String], forPreferences preferences: [String]?) -> [String] {
146
+ guard !available.isEmpty else { return [] }
147
+ func normalized(_ tag: String) -> String { tag.replacingOccurrences(of: "_", with: "-").lowercased() }
148
+ func language(_ tag: String) -> Substring { normalized(tag).split(separator: "-").first ?? "" }
149
+ for preference in preferences ?? [] {
150
+ if let exact = available.first(where: { normalized($0) == normalized(preference) }) { return [exact] }
151
+ if let script = chineseScript(preference), let match = available.first(where: { normalized($0) == "zh-\(script)" }) { return [match] }
152
+ if let close = available.first(where: { language($0) == language(preference) && !normalized($0).contains("-") }) { return [close] }
153
+ if let close = available.first(where: { language($0) == language(preference) }) { return [close] }
154
+ }
155
+ if available.contains("en") { return ["en"] }
156
+ return [available[0]]
157
+ }
158
+
159
+ /// `hant` for traditional Chinese (`zh-TW`, `zh-HK`, `zh-MO`, `zh-Hant…`), `hans` for the rest of `zh`.
160
+ private static func chineseScript(_ tag: String) -> String? {
161
+ let parts = tag.replacingOccurrences(of: "_", with: "-").lowercased().split(separator: "-")
162
+ guard parts.first == "zh" else { return nil }
163
+ if parts.contains("hant") || parts.contains("tw") || parts.contains("hk") || parts.contains("mo") { return "hant" }
164
+ return "hans"
165
+ }
166
+
167
+ /// The language the app's strings are written in. Xcode records it in the
168
+ /// app's Info.plist; the CLI ships its tables, and English is the default.
169
+ static func developmentLocalization(among available: [String]) -> String? {
170
+ available.contains("en") ? "en" : available.first
171
+ }
172
+
173
+ // MARK: Tables
174
+
175
+ /// Loaded tables and bundles' languages, behind a lock: views on any
176
+ /// thread look strings up (tests run in parallel on the host).
177
+ private static let cacheLock = NSLock()
178
+ nonisolated(unsafe) private static var tables: [String: [String: _LocalizedEntry]] = [:]
179
+ nonisolated(unsafe) private static var languagesByBundle: [String: [String]] = [:]
180
+
181
+ /// The entries of `<bundle>/<language>.lproj/<table>.strings` and `.stringsdict` (empty when absent).
182
+ static func table(_ name: String, language: String, bundlePath: String) -> [String: _LocalizedEntry] {
183
+ let base = "\(bundlePath)/\(language).lproj/\(name)"
184
+ cacheLock.lock()
185
+ defer { cacheLock.unlock() }
186
+ if let cached = tables[base] { return cached }
187
+ var entries: [String: _LocalizedEntry] = [:]
188
+ if let data = FileManager.default.contents(atPath: "\(base).strings"),
189
+ let strings = try? PropertyListDecoder().decode([String: String].self, from: data) {
190
+ for (key, value) in strings { entries[key] = .format(value) }
191
+ }
192
+ if let data = FileManager.default.contents(atPath: "\(base).stringsdict"),
193
+ let rules = try? PropertyListDecoder().decode([String: [String: _PlistValue]].self, from: data) {
194
+ for (key, rule) in rules {
195
+ guard case .string(let formatKey)? = rule["NSStringLocalizedFormatKey"] else { continue }
196
+ var variables: [String: [String: String]] = [:]
197
+ for (name, value) in rule where name != "NSStringLocalizedFormatKey" {
198
+ if case .dictionary(let cases) = value { variables[name] = cases }
199
+ }
200
+ entries[key] = .rule(formatKey: formatKey, variables: variables)
201
+ }
202
+ }
203
+ tables[base] = entries
204
+ return entries
205
+ }
206
+
207
+ /// Forgets the loaded tables and languages (tests write new ones).
208
+ public static func _resetTables() {
209
+ cacheLock.lock()
210
+ defer { cacheLock.unlock() }
211
+ tables = [:]
212
+ languagesByBundle = [:]
213
+ }
214
+
215
+ /// The entry for `key`: the running language's table, then the development language's.
216
+ /// `language` overrides the viewer's (a test's, or a resource's locale).
217
+ static func entry(forKey key: String, table: String?, bundlePath: String, language: String? = nil) -> (entry: _LocalizedEntry, language: String)? {
218
+ let available = localizations(bundlePath: bundlePath)
219
+ guard !available.isEmpty else { return nil }
220
+ let name = (table?.isEmpty ?? true) ? "Localizable" : table!
221
+ var languages: [String] = []
222
+ let preferred = language.flatMap { preferredLocalizations(from: available, forPreferences: [$0]).first } ?? preferredLocalization(from: available)
223
+ if let preferred { languages.append(preferred) }
224
+ if let development = developmentLocalization(among: available), !languages.contains(development) { languages.append(development) }
225
+ for language in languages {
226
+ if let entry = self.table(name, language: language, bundlePath: bundlePath)[key] { return (entry, language) }
227
+ }
228
+ return nil
229
+ }
230
+
231
+ /// The language plural rules follow: the one the app runs in.
232
+ static func runningLanguage(bundlePath: String) -> String {
233
+ preferredLocalization(from: localizations(bundlePath: bundlePath)) ?? _environment("SB_LOCALE") ?? "en"
234
+ }
235
+
236
+ // MARK: Resolving
237
+
238
+ /// `Bundle.localizedString(forKey:value:table:)`: the key's format string
239
+ /// (a rule's, with its tokens resolved for no particular count, as
240
+ /// `other`), else `value` when it is not empty, else the key.
241
+ public static func localizedString(forKey key: String, value: String?, table: String?, bundlePath: String) -> String {
242
+ switch entry(forKey: key, table: table, bundlePath: bundlePath) {
243
+ case (.format(let format), _)?: return format
244
+ case (.rule(let formatKey, let variables), let language)?:
245
+ return expand(formatKey, variables: variables, arguments: [], language: language)
246
+ case nil:
247
+ if let value, !value.isEmpty { return value }
248
+ return key
249
+ }
250
+ }
251
+
252
+ /// `key` localized and formatted with `arguments`: the pieces of text, and
253
+ /// the indices of the arguments (nil entries: views) that go between them.
254
+ /// A key with no arguments is not formatted (`"100%"` stays as it is).
255
+ /// `defaultValue` is the format when the tables lack the key (else the key itself).
256
+ public static func resolve(key: String, arguments: [_LocalizationArgument?], table: String? = nil,
257
+ bundlePath: String? = nil, defaultValue: String? = nil, language: String? = nil) -> [_LocalizedPiece] {
258
+ let path = bundlePath ?? mainBundlePath
259
+ let found = entry(forKey: key, table: table, bundlePath: path, language: language)
260
+ let format: String
261
+ switch found {
262
+ case (.format(let text), _)?: format = text
263
+ case (.rule(let formatKey, let variables), let language)?:
264
+ format = expand(formatKey, variables: variables, arguments: arguments, language: language)
265
+ case nil: format = defaultValue ?? key
266
+ }
267
+ if arguments.isEmpty { return [.text(format)] }
268
+ return _LocalizedFormat.format(format, arguments: arguments)
269
+ }
270
+
271
+ /// `resolve` as one string, views left out.
272
+ public static func string(key: String, arguments: [_LocalizationArgument?], table: String? = nil, bundlePath: String? = nil,
273
+ defaultValue: String? = nil, language: String? = nil) -> String {
274
+ resolve(key: key, arguments: arguments, table: table, bundlePath: bundlePath, defaultValue: defaultValue, language: language).map { piece in
275
+ if case .text(let text) = piece { text } else { "" }
276
+ }.joined()
277
+ }
278
+
279
+ /// `key` formatted with `arguments`, not looked up (a key with no arguments is its own text).
280
+ public static func formatted(key: String, arguments: [_LocalizationArgument]) -> String {
281
+ if arguments.isEmpty { return key }
282
+ return _LocalizedFormat.format(key, arguments: arguments).map { piece in
283
+ if case .text(let text) = piece { text } else { "" }
284
+ }.joined()
285
+ }
286
+
287
+ /// A rule's format key with each `%#@name@` token replaced by the case its
288
+ /// variable picks for the token's argument (plural rules), repeatedly, as
289
+ /// cases may hold tokens. Every specifier is numbered first, so a case's
290
+ /// own unnumbered specifiers take its token's argument and the others keep theirs.
291
+ static func expand(_ formatKey: String, variables: [String: [String: String]], arguments: [_LocalizationArgument?], language: String) -> String {
292
+ var format = _LocalizedFormat.numbered(formatKey)
293
+ for _ in 0..<8 {
294
+ guard let token = _LocalizedFormat.firstToken(in: format) else { break }
295
+ let variable = variables[token.name] ?? [:]
296
+ let argument = token.position - 1 < arguments.count ? arguments[token.position - 1] : nil
297
+ let chosen: String
298
+ if variable["NSStringFormatSpecTypeKey"] == "NSStringPluralRuleType" {
299
+ let number = argument?.number
300
+ let category = number.map { _PluralRules.category(for: $0, language: language) } ?? "other"
301
+ if number == 0, let zero = variable["zero"] {
302
+ chosen = zero
303
+ } else {
304
+ chosen = variable[category] ?? variable["other"] ?? ""
305
+ }
306
+ } else {
307
+ // A device rule (settled at compile time) or one the browser has no use for.
308
+ chosen = variable["iphone"] ?? variable["other"] ?? ""
309
+ }
310
+ let replacement = _LocalizedFormat.numbered(chosen, defaultPosition: token.position)
311
+ format.replaceSubrange(token.range, with: replacement)
312
+ }
313
+ return format
314
+ }
315
+ }
316
+
317
+ /// A property list value of a `.stringsdict`: a string or a dictionary of strings.
318
+ enum _PlistValue: Decodable {
319
+ case string(String)
320
+ case dictionary([String: String])
321
+ case other
322
+
323
+ init(from decoder: any Decoder) throws {
324
+ let container = try decoder.singleValueContainer()
325
+ if let string = try? container.decode(String.self) {
326
+ self = .string(string)
327
+ } else if let dictionary = try? container.decode([String: String].self) {
328
+ self = .dictionary(dictionary)
329
+ } else {
330
+ self = .other
331
+ }
332
+ }
333
+ }
334
+
335
+ // MARK: - Formatting
336
+
337
+ enum _LocalizedFormat {
338
+ /// One `%` specifier: its range, explicit position (`%2$@`), the rest of
339
+ /// the spec without the position (`-5`, `.2`, `ll`) and its conversion.
340
+ struct Specifier {
341
+ let range: Range<String.Index>
342
+ let position: Int?
343
+ let spec: String
344
+ let conversion: Character
345
+ /// `%#@name@`: a stringsdict token.
346
+ let token: String?
347
+ }
348
+
349
+ /// The specifiers of a format string, in order (`%%` left out).
350
+ static func specifiers(in format: String) -> [Specifier] {
351
+ var out: [Specifier] = []
352
+ var index = format.startIndex
353
+ while let percent = format[index...].firstIndex(of: "%") {
354
+ var cursor = format.index(after: percent)
355
+ guard cursor < format.endIndex else { break }
356
+ if format[cursor] == "%" {
357
+ index = format.index(after: cursor)
358
+ continue
359
+ }
360
+ // Position: digits followed by `$`.
361
+ var position: Int?
362
+ var digits = cursor
363
+ while digits < format.endIndex, format[digits].isASCII, format[digits].isNumber { digits = format.index(after: digits) }
364
+ if digits > cursor, digits < format.endIndex, format[digits] == "$" {
365
+ position = Int(format[cursor..<digits])
366
+ cursor = format.index(after: digits)
367
+ }
368
+ let specStart = cursor
369
+ if cursor < format.endIndex, format[cursor] == "#", format.index(after: cursor) < format.endIndex, format[format.index(after: cursor)] == "@" {
370
+ // `%#@name@`
371
+ let nameStart = format.index(cursor, offsetBy: 2)
372
+ if let end = format[nameStart...].firstIndex(of: "@") {
373
+ out.append(Specifier(range: percent..<format.index(after: end), position: position, spec: "",
374
+ conversion: "@", token: String(format[nameStart..<end])))
375
+ index = format.index(after: end)
376
+ continue
377
+ }
378
+ }
379
+ while cursor < format.endIndex, "-+ #0'".contains(format[cursor]) { cursor = format.index(after: cursor) }
380
+ while cursor < format.endIndex, format[cursor].isASCII, format[cursor].isNumber || format[cursor] == "*" { cursor = format.index(after: cursor) }
381
+ if cursor < format.endIndex, format[cursor] == "." {
382
+ cursor = format.index(after: cursor)
383
+ while cursor < format.endIndex, format[cursor].isASCII, format[cursor].isNumber { cursor = format.index(after: cursor) }
384
+ }
385
+ while cursor < format.endIndex, "hlqLztj".contains(format[cursor]) { cursor = format.index(after: cursor) }
386
+ guard cursor < format.endIndex else { break }
387
+ let conversion = format[cursor]
388
+ guard "@dDiuUxXoOfFeEgGaAcCsSp".contains(conversion) else {
389
+ index = format.index(after: cursor)
390
+ continue
391
+ }
392
+ out.append(Specifier(range: percent..<format.index(after: cursor), position: position,
393
+ spec: String(format[specStart..<cursor]), conversion: conversion, token: nil))
394
+ index = format.index(after: cursor)
395
+ }
396
+ return out
397
+ }
398
+
399
+ /// `format` with every unnumbered specifier numbered: in order from 1, or
400
+ /// all `defaultPosition` (a plural case's specifiers are its token's argument).
401
+ static func numbered(_ format: String, defaultPosition: Int? = nil) -> String {
402
+ var result = ""
403
+ var last = format.startIndex
404
+ var next = 1
405
+ for specifier in specifiers(in: format) {
406
+ result += format[last..<specifier.range.lowerBound]
407
+ let position = specifier.position ?? defaultPosition ?? next
408
+ if specifier.position == nil, defaultPosition == nil { next += 1 }
409
+ if let token = specifier.token {
410
+ result += "%\(position)$#@\(token)@"
411
+ } else {
412
+ result += "%\(position)$\(specifier.spec)\(specifier.conversion)"
413
+ }
414
+ last = specifier.range.upperBound
415
+ }
416
+ result += format[last...]
417
+ return result
418
+ }
419
+
420
+ static func firstToken(in format: String) -> (name: String, position: Int, range: Range<String.Index>)? {
421
+ for specifier in specifiers(in: format) {
422
+ if let token = specifier.token { return (token, specifier.position ?? 1, specifier.range) }
423
+ }
424
+ return nil
425
+ }
426
+
427
+ /// The pieces of `format` with its specifiers filled: unnumbered ones take
428
+ /// the arguments in order, numbered ones theirs; a view argument (nil)
429
+ /// becomes an `.argument` piece; `%%` is `%`.
430
+ static func format(_ format: String, arguments: [_LocalizationArgument?]) -> [_LocalizedPiece] {
431
+ var pieces: [_LocalizedPiece] = []
432
+ var text = ""
433
+ var last = format.startIndex
434
+ var next = 0
435
+ func literal(_ part: Substring) { text += part.replacingOccurrences(of: "%%", with: "%") }
436
+ for specifier in specifiers(in: format) {
437
+ literal(format[last..<specifier.range.lowerBound])
438
+ last = specifier.range.upperBound
439
+ let index: Int
440
+ if let position = specifier.position {
441
+ index = position - 1
442
+ } else {
443
+ index = next
444
+ next += 1
445
+ }
446
+ guard index >= 0, index < arguments.count else { continue }
447
+ guard let argument = arguments[index] else {
448
+ if !text.isEmpty { pieces.append(.text(text)); text = "" }
449
+ pieces.append(.argument(index))
450
+ continue
451
+ }
452
+ text += render(argument, spec: specifier.spec, conversion: specifier.conversion)
453
+ }
454
+ literal(format[last...])
455
+ if !text.isEmpty || pieces.isEmpty { pieces.append(.text(text)) }
456
+ return pieces
457
+ }
458
+
459
+ /// A formatted number's integer digits grouped with the locale's separator
460
+ /// and its decimal point the locale's, as Apple's localized formatting
461
+ /// (`String.localizedStringWithFormat`, which `Text` and
462
+ /// `String(localized:)` use) writes `%d`, `%u` and `%f`: `"\(1024)"` is
463
+ /// "1,024" in English (and a year "2,026", as on iOS).
464
+ static func localizedDigits(_ text: String) -> String {
465
+ let locale = Locale.current
466
+ let grouping = locale.groupingSeparator ?? ","
467
+ let decimal = locale.decimalSeparator ?? "."
468
+ guard let start = text.firstIndex(where: \.isNumber) else { return text }
469
+ let end = text[start...].firstIndex(where: { !$0.isNumber }) ?? text.endIndex
470
+ let digits = text[start..<end]
471
+ var grouped = ""
472
+ for (offset, digit) in digits.enumerated() {
473
+ if offset > 0 && (digits.count - offset) % 3 == 0 { grouped += grouping }
474
+ grouped.append(digit)
475
+ }
476
+ var rest = String(text[end...])
477
+ if rest.hasPrefix(".") { rest = decimal + rest.dropFirst() }
478
+ return String(text[..<start]) + grouped + rest
479
+ }
480
+
481
+ /// One argument as its specifier formats it.
482
+ static func render(_ argument: _LocalizationArgument, spec: String, conversion: Character) -> String {
483
+ let flags = spec.filter { !"hlqLztj".contains($0) }
484
+ switch conversion {
485
+ case "@", "s", "S":
486
+ return argument.text
487
+ case "d", "D", "i":
488
+ let value: Int64 = switch argument {
489
+ case .int(let v): v
490
+ case .uint(let v): Int64(truncatingIfNeeded: v)
491
+ case .double(let v): v.isFinite ? Int64(v) : 0
492
+ case .string(let v): Int64(v) ?? 0
493
+ }
494
+ return flags.isEmpty ? localizedDigits(String(value)) : String(format: "%\(flags)lld", value)
495
+ case "u", "U", "x", "X", "o", "O":
496
+ let value: UInt64 = switch argument {
497
+ case .int(let v): UInt64(bitPattern: v)
498
+ case .uint(let v): v
499
+ case .double(let v): v.isFinite && v >= 0 ? UInt64(v) : 0
500
+ case .string(let v): UInt64(v) ?? 0
501
+ }
502
+ let lower = Character(conversion.lowercased())
503
+ let code = lower == "u" ? "u" : String(conversion)
504
+ if lower == "u" && flags.isEmpty { return localizedDigits(String(value)) }
505
+ return String(format: "%\(flags)ll\(code)", value)
506
+ case "f", "F", "e", "E", "g", "G", "a", "A":
507
+ let value: Double = switch argument {
508
+ case .int(let v): Double(v)
509
+ case .uint(let v): Double(v)
510
+ case .double(let v): v
511
+ case .string(let v): Double(v) ?? 0
512
+ }
513
+ let text = String(format: "%\(flags)\(conversion)", value)
514
+ // A precision alone (`%.1f`) keeps the locale's digits; a width or a flag pads as given.
515
+ let plain = flags.isEmpty || flags.first == "." && flags.dropFirst().allSatisfy(\.isNumber)
516
+ return plain && (conversion == "f" || conversion == "F") ? localizedDigits(text) : text
517
+ case "c", "C":
518
+ if case .int(let v) = argument, let scalar = Unicode.Scalar(UInt32(truncatingIfNeeded: v)) { return String(Character(scalar)) }
519
+ return argument.text
520
+ default:
521
+ return argument.text
522
+ }
523
+ }
524
+ }
@@ -0,0 +1,127 @@
1
+ // CLDR's cardinal plural rules, which pick a `.stringsdict` plural case
2
+ // (`zero`, `one`, `two`, `few`, `many`, `other`) for a count in a language.
3
+ // The operands follow CLDR: `n` the absolute value, `i` its integer digits,
4
+ // `v` whether it has visible fraction digits (a `Double` with a fractional
5
+ // part; the browser cannot tell `1.0` from `1`).
6
+
7
+ import Foundation
8
+
9
+ enum _PluralRules {
10
+ static func category(for value: Double, language tag: String) -> String {
11
+ let n = abs(value)
12
+ let i = n.rounded(.towardZero)
13
+ let fractional = n != i
14
+ let integer = Int64(clamping: Int(exactly: i) ?? Int.max)
15
+ let i10 = integer % 10
16
+ let i100 = integer % 100
17
+ let language = tag.replacingOccurrences(of: "_", with: "-").lowercased().split(separator: "-").first.map(String.init) ?? ""
18
+ let millions = !fractional && integer != 0 && integer % 1_000_000 == 0
19
+
20
+ switch language {
21
+ // No plural forms.
22
+ case "ja", "zh", "ko", "th", "vi", "id", "ms", "my", "lo", "km", "yue", "jv", "su":
23
+ return "other"
24
+ case "fr":
25
+ if !fractional && (integer == 0 || integer == 1) { return "one" }
26
+ if fractional && i < 2 { return "one" }
27
+ return millions ? "many" : "other"
28
+ case "pt":
29
+ if tag.lowercased().hasPrefix("pt-pt") {
30
+ if !fractional && integer == 1 { return "one" }
31
+ return millions ? "many" : "other"
32
+ }
33
+ if i < 2 { return "one" }
34
+ return millions ? "many" : "other"
35
+ case "es", "it", "ca":
36
+ if !fractional && integer == 1 { return "one" }
37
+ return millions ? "many" : "other"
38
+ case "ru", "uk", "be":
39
+ if fractional { return "other" }
40
+ if i10 == 1 && i100 != 11 { return "one" }
41
+ if (2...4).contains(i10) && !(12...14).contains(i100) { return "few" }
42
+ return "many"
43
+ case "pl":
44
+ if fractional { return "other" }
45
+ if integer == 1 { return "one" }
46
+ if (2...4).contains(i10) && !(12...14).contains(i100) { return "few" }
47
+ return "many"
48
+ case "cs", "sk":
49
+ if fractional { return "many" }
50
+ if integer == 1 { return "one" }
51
+ if (2...4).contains(integer) { return "few" }
52
+ return "other"
53
+ case "hr", "sr", "bs", "sh":
54
+ if fractional { return "other" }
55
+ if i10 == 1 && i100 != 11 { return "one" }
56
+ if (2...4).contains(i10) && !(12...14).contains(i100) { return "few" }
57
+ return "other"
58
+ case "lt":
59
+ if fractional { return "many" }
60
+ if i10 == 1 && !(11...19).contains(i100) { return "one" }
61
+ if (2...9).contains(i10) && !(11...19).contains(i100) { return "few" }
62
+ return "other"
63
+ case "lv":
64
+ if fractional { return "other" }
65
+ if i10 == 0 || (11...19).contains(i100) { return "zero" }
66
+ if i10 == 1 && i100 != 11 { return "one" }
67
+ return "other"
68
+ case "ro", "mo":
69
+ if !fractional && integer == 1 { return "one" }
70
+ if fractional || integer == 0 || (2...19).contains(i100) { return "few" }
71
+ return "other"
72
+ case "sl":
73
+ if fractional { return "few" }
74
+ if i100 == 1 { return "one" }
75
+ if i100 == 2 { return "two" }
76
+ if i100 == 3 || i100 == 4 { return "few" }
77
+ return "other"
78
+ case "ar":
79
+ if fractional { return "other" }
80
+ if integer == 0 { return "zero" }
81
+ if integer == 1 { return "one" }
82
+ if integer == 2 { return "two" }
83
+ if (3...10).contains(i100) { return "few" }
84
+ if (11...99).contains(i100) { return "many" }
85
+ return "other"
86
+ case "he", "iw":
87
+ if !fractional && integer == 1 { return "one" }
88
+ if !fractional && integer == 2 { return "two" }
89
+ return "other"
90
+ case "ga":
91
+ if fractional { return "other" }
92
+ switch integer {
93
+ case 1: return "one"
94
+ case 2: return "two"
95
+ case 3...6: return "few"
96
+ case 7...10: return "many"
97
+ default: return "other"
98
+ }
99
+ case "cy":
100
+ if fractional { return "other" }
101
+ switch integer {
102
+ case 0: return "zero"
103
+ case 1: return "one"
104
+ case 2: return "two"
105
+ case 3: return "few"
106
+ case 6: return "many"
107
+ default: return "other"
108
+ }
109
+ case "da":
110
+ if n == 1 || (fractional && (i == 0 || i == 1)) { return "one" }
111
+ return "other"
112
+ case "is":
113
+ if !fractional && i10 == 1 && i100 != 11 { return "one" }
114
+ return "other"
115
+ case "mk":
116
+ if !fractional && i10 == 1 && i100 != 11 { return "one" }
117
+ return "other"
118
+ case "hi", "bn", "gu", "kn", "mr", "fa", "zu", "am":
119
+ if i == 0 || n == 1 { return "one" }
120
+ return "other"
121
+ default:
122
+ // English and most languages: one for exactly 1.
123
+ if !fractional && integer == 1 { return "one" }
124
+ return "other"
125
+ }
126
+ }
127
+ }
@@ -64,6 +64,8 @@ final class _HostExecutor: MainExecutor, SchedulingExecutor, TaskExecutor, @unch
64
64
  }
65
65
 
66
66
  var hasTimers: Bool { !timers.isEmpty }
67
+ /// Whether jobs are waiting to run now (enqueued since the last `runJobs`).
68
+ var hasQueuedJobs: Bool { !queue.isEmpty }
67
69
  }
68
70
 
69
71
  enum _HostExecutorFactory: ExecutorFactory {