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