@swiftbrowser/swift 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Package.swift +214 -0
- package/README.md +10 -2
- 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 +61 -0
- 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/FoundationBridge/Bundle.swift +119 -0
- 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 +29 -0
- 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/Locale.swift +86 -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 +53 -0
- package/Sources/FoundationBridge/UITextChecker.swift +30 -0
- package/Sources/FoundationBridge/URLSession.swift +718 -0
- package/Sources/FoundationBridge/UserDefaults.swift +183 -0
- 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 +181 -0
- package/Sources/SwiftUI/API/AccessibilityExtras.swift +33 -0
- package/Sources/SwiftUI/API/AccessibilityFocus.swift +63 -0
- package/Sources/SwiftUI/API/Alert.swift +221 -0
- package/Sources/SwiftUI/API/AlignmentGuides.swift +65 -0
- package/Sources/SwiftUI/API/Animation.swift +285 -0
- package/Sources/SwiftUI/API/App.swift +87 -0
- 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 +237 -0
- 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 +300 -0
- package/Sources/SwiftUI/API/CustomLayout.swift +442 -0
- package/Sources/SwiftUI/API/CustomToolbarContent.swift +46 -0
- package/Sources/SwiftUI/API/Decorations.swift +92 -0
- package/Sources/SwiftUI/API/EditMode.swift +212 -0
- package/Sources/SwiftUI/API/Effects.swift +388 -0
- package/Sources/SwiftUI/API/Entry.swift +8 -0
- package/Sources/SwiftUI/API/Environment.swift +439 -0
- package/Sources/SwiftUI/API/EnvironmentSendable.swift +6 -0
- package/Sources/SwiftUI/API/Focus.swift +137 -0
- package/Sources/SwiftUI/API/Font.swift +131 -0
- package/Sources/SwiftUI/API/Gauge.swift +290 -0
- package/Sources/SwiftUI/API/Geometry.swift +89 -0
- package/Sources/SwiftUI/API/GeometryEffects.swift +232 -0
- package/Sources/SwiftUI/API/GeometryReader.swift +135 -0
- package/Sources/SwiftUI/API/GestureState.swift +65 -0
- package/Sources/SwiftUI/API/Gestures.swift +342 -0
- package/Sources/SwiftUI/API/Glass.swift +127 -0
- package/Sources/SwiftUI/API/Grid.swift +188 -0
- package/Sources/SwiftUI/API/GroupBoxAndControlGroup.swift +130 -0
- package/Sources/SwiftUI/API/KeyPress.swift +54 -0
- package/Sources/SwiftUI/API/LabelStyle.swift +124 -0
- package/Sources/SwiftUI/API/Layout.swift +220 -0
- package/Sources/SwiftUI/API/LazyStacks.swift +49 -0
- package/Sources/SwiftUI/API/LegacyAlert.swift +97 -0
- package/Sources/SwiftUI/API/Lists.swift +331 -0
- package/Sources/SwiftUI/API/Menu.swift +58 -0
- package/Sources/SwiftUI/API/Modifiers.swift +313 -0
- package/Sources/SwiftUI/API/MoreModifiers.swift +100 -0
- package/Sources/SwiftUI/API/Namespace.swift +44 -0
- package/Sources/SwiftUI/API/Navigation.swift +386 -0
- package/Sources/SwiftUI/API/NavigationDestinationBindings.swift +55 -0
- package/Sources/SwiftUI/API/ObservableObject.swift +293 -0
- package/Sources/SwiftUI/API/OnChange.swift +48 -0
- package/Sources/SwiftUI/API/Path.swift +543 -0
- package/Sources/SwiftUI/API/Phase8Controls.swift +411 -0
- package/Sources/SwiftUI/API/PhaseAnimator.swift +130 -0
- package/Sources/SwiftUI/API/Picker.swift +185 -0
- 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 +122 -0
- package/Sources/SwiftUI/API/PresentationExtras.swift +60 -0
- package/Sources/SwiftUI/API/Primitives.swift +517 -0
- package/Sources/SwiftUI/API/Publishers.swift +107 -0
- package/Sources/SwiftUI/API/Redaction.swift +75 -0
- package/Sources/SwiftUI/API/RowActions.swift +84 -0
- package/Sources/SwiftUI/API/Scenes.swift +131 -0
- package/Sources/SwiftUI/API/ScrollModifiers.swift +132 -0
- package/Sources/SwiftUI/API/ScrollTargets.swift +104 -0
- 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 +409 -0
- 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 +144 -0
- package/Sources/SwiftUI/API/StatusGaps.swift +121 -0
- package/Sources/SwiftUI/API/StrokeStyle.swift +30 -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 +263 -0
- package/Sources/SwiftUI/API/Tasks.swift +35 -0
- 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/Timeline.swift +212 -0
- package/Sources/SwiftUI/API/Toolbar.swift +251 -0
- 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 +215 -0
- package/Sources/SwiftUI/API/ViewModifier.swift +43 -0
- package/Sources/SwiftUI/API/VisualEffect.swift +155 -0
- package/Sources/SwiftUI/API/Windows.swift +25 -0
- package/Sources/SwiftUI/Charts/Axis.swift +325 -0
- package/Sources/SwiftUI/Charts/AxisFormatsAndRanges.swift +68 -0
- package/Sources/SwiftUI/Charts/Chart.swift +660 -0
- package/Sources/SwiftUI/Charts/ChartContent.swift +376 -0
- package/Sources/SwiftUI/Charts/ChartProxy.swift +57 -0
- package/Sources/SwiftUI/Charts/Marks.swift +204 -0
- package/Sources/SwiftUI/Charts/Plottable.swift +99 -0
- package/Sources/SwiftUI/Charts/Scales.swift +326 -0
- package/Sources/SwiftUI/Localization/Localization.swift +498 -0
- package/Sources/SwiftUI/Localization/PluralRules.swift +127 -0
- package/Sources/SwiftUI/Runtime/Executor.swift +85 -0
- package/Sources/SwiftUI/Runtime/Exports.swift +101 -0
- package/Sources/SwiftUI/Runtime/ImageSource.swift +310 -0
- package/Sources/SwiftUI/Runtime/JSON.swift +143 -0
- package/Sources/SwiftUI/Runtime/JSONParser.swift +443 -0
- package/Sources/SwiftUI/Runtime/LazyRows.swift +99 -0
- package/Sources/SwiftUI/Runtime/Measurements.swift +140 -0
- package/Sources/SwiftUI/Runtime/Ops.swift +152 -0
- package/Sources/SwiftUI/Runtime/Reconciler.swift +219 -0
- package/Sources/SwiftUI/Runtime/Replay.swift +382 -0
- package/Sources/SwiftUI/Runtime/Runtime.swift +2146 -0
- 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 +2 -0
- package/index.mjs +16 -0
- package/manifest.d.mts +2 -0
- package/manifest.mjs +39 -0
- package/package.json +30 -4
- package/sync-sources.mjs +33 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// The structure of a GIF file, as Image I/O reports it: how many frames it
|
|
2
|
+
// has, each frame's delay and the number of times it loops. Reading it walks
|
|
3
|
+
// the file's blocks without decoding a pixel; the page decodes and composes
|
|
4
|
+
// a frame when it draws it (Web/src/gif.ts, docs/ops-protocol.md, "Animated
|
|
5
|
+
// GIFs").
|
|
6
|
+
|
|
7
|
+
/// A GIF's frames and loop count.
|
|
8
|
+
struct _GIFStructure: Equatable {
|
|
9
|
+
struct Frame: Equatable {
|
|
10
|
+
/// The graphic control extension's delay, in hundredths of a second.
|
|
11
|
+
var delay: Int
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
var frames: [Frame]
|
|
15
|
+
/// The Netscape application extension's count: 0 loops forever; nil when
|
|
16
|
+
/// the file has none (it plays once).
|
|
17
|
+
var loopCount: Int?
|
|
18
|
+
var hasGlobalColorMap: Bool
|
|
19
|
+
|
|
20
|
+
/// The structure of a GIF file; nil for anything else. A file cut short
|
|
21
|
+
/// has the frames that start before the cut.
|
|
22
|
+
static func parse(_ bytes: [UInt8]) -> _GIFStructure? {
|
|
23
|
+
guard bytes.count >= 13, bytes.starts(with: Array("GIF87a".utf8)) || bytes.starts(with: Array("GIF89a".utf8)) else { return nil }
|
|
24
|
+
let screen = bytes[10]
|
|
25
|
+
let hasGlobalColorMap = screen & 0x80 != 0
|
|
26
|
+
var index = 13 + (hasGlobalColorMap ? 3 << (Int(screen & 0x07) + 1) : 0)
|
|
27
|
+
var structure = _GIFStructure(frames: [], loopCount: nil, hasGlobalColorMap: hasGlobalColorMap)
|
|
28
|
+
var delay = 0
|
|
29
|
+
|
|
30
|
+
/// The index after the data sub-blocks starting at `start`; nil when the file ends first.
|
|
31
|
+
func skipSubBlocks(_ start: Int) -> Int? {
|
|
32
|
+
var at = start
|
|
33
|
+
while at < bytes.count {
|
|
34
|
+
let size = Int(bytes[at])
|
|
35
|
+
if size == 0 { return at + 1 }
|
|
36
|
+
at += 1 + size
|
|
37
|
+
}
|
|
38
|
+
return nil
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
while index < bytes.count {
|
|
42
|
+
switch bytes[index] {
|
|
43
|
+
case 0x21: // An extension: its label, then data sub-blocks.
|
|
44
|
+
guard index + 1 < bytes.count else { return structure }
|
|
45
|
+
let label = bytes[index + 1]
|
|
46
|
+
let body = index + 2
|
|
47
|
+
if label == 0xF9, body + 4 < bytes.count, bytes[body] >= 4 {
|
|
48
|
+
delay = Int(bytes[body + 2]) | Int(bytes[body + 3]) << 8
|
|
49
|
+
}
|
|
50
|
+
if label == 0xFF, body + 16 < bytes.count, bytes[body] == 11,
|
|
51
|
+
bytes[(body + 1)...(body + 11)].elementsEqual(Array("NETSCAPE2.0".utf8)) || bytes[(body + 1)...(body + 11)].elementsEqual(Array("ANIMEXTS1.0".utf8)),
|
|
52
|
+
bytes[body + 12] >= 3, bytes[body + 13] == 1 {
|
|
53
|
+
structure.loopCount = Int(bytes[body + 14]) | Int(bytes[body + 15]) << 8
|
|
54
|
+
}
|
|
55
|
+
guard let next = skipSubBlocks(body) else { return structure }
|
|
56
|
+
index = next
|
|
57
|
+
case 0x2C: // An image: its descriptor, a local color table, the LZW code size, the data.
|
|
58
|
+
guard index + 9 < bytes.count else { return structure }
|
|
59
|
+
let packed = bytes[index + 9]
|
|
60
|
+
var at = index + 10
|
|
61
|
+
if packed & 0x80 != 0 { at += 3 << (Int(packed & 0x07) + 1) }
|
|
62
|
+
guard at < bytes.count, let next = skipSubBlocks(at + 1) else { return structure }
|
|
63
|
+
structure.frames.append(Frame(delay: delay))
|
|
64
|
+
delay = 0
|
|
65
|
+
index = next
|
|
66
|
+
default: // The trailer (0x3B), or something that is not a block.
|
|
67
|
+
return structure
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return structure
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
// `import ImageIO` for the browser: image sources over the shim's decoder.
|
|
2
|
+
// `CGImageSourceCreateWithData` reads the file's header (format, size)
|
|
3
|
+
// without decoding it; an image made from the source is a `CGImage` whose
|
|
4
|
+
// recipe is the file, which the page decodes when it draws it (with its
|
|
5
|
+
// EXIF rotation applied, so the properties report `.up`). An animated GIF
|
|
6
|
+
// has one image per frame, with its delay and the file's loop count in the
|
|
7
|
+
// properties (GIF.swift); the page composes the frame. Thumbnails are the
|
|
8
|
+
// image scaled to the requested size. Incremental sources produce no image
|
|
9
|
+
// until the data is complete. Destinations (encoding) are not available:
|
|
10
|
+
// creating one fails. See docs/ops-protocol.md, "Core Graphics and Image I/O"
|
|
11
|
+
// and "Animated GIFs".
|
|
12
|
+
|
|
13
|
+
@_exported import CoreGraphics
|
|
14
|
+
import struct Foundation.Data
|
|
15
|
+
import struct Foundation.URL
|
|
16
|
+
import class Foundation.NSMutableData
|
|
17
|
+
import SwiftUI
|
|
18
|
+
|
|
19
|
+
// Core Foundation's types, as Image I/O takes them (`data as CFData`,
|
|
20
|
+
// `options as CFDictionary`: plain Swift values here).
|
|
21
|
+
@_exported import CoreFoundationTypes
|
|
22
|
+
|
|
23
|
+
/// An image file, or the bytes of one so far.
|
|
24
|
+
public final class CGImageSource: @unchecked Sendable {
|
|
25
|
+
var data: Data {
|
|
26
|
+
didSet { read() }
|
|
27
|
+
}
|
|
28
|
+
var header: _ImageHeader?
|
|
29
|
+
/// A GIF's frames, read once the data is set.
|
|
30
|
+
var gif: _GIFStructure?
|
|
31
|
+
let isIncremental: Bool
|
|
32
|
+
var isFinal: Bool
|
|
33
|
+
|
|
34
|
+
init(data: Data, incremental: Bool, final: Bool) {
|
|
35
|
+
self.data = data
|
|
36
|
+
self.isIncremental = incremental
|
|
37
|
+
self.isFinal = final
|
|
38
|
+
read()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private func read() {
|
|
42
|
+
header = _ImageHeader.parse([UInt8](data.prefix(64 * 1024)))
|
|
43
|
+
gif = header?.mime == "image/gif" ? _GIFStructure.parse([UInt8](data)) : nil
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// The number of images: a GIF's frames (at least one once its header is
|
|
47
|
+
/// read), one for any other file.
|
|
48
|
+
var count: Int {
|
|
49
|
+
guard header != nil else { return 0 }
|
|
50
|
+
return max(1, gif?.frames.count ?? 1)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// Whether the images are frames the page composes (an animated GIF).
|
|
54
|
+
var isAnimated: Bool { (gif?.frames.count ?? 0) > 1 }
|
|
55
|
+
|
|
56
|
+
/// The decoded file, ready to draw.
|
|
57
|
+
var source: _ImageSource? {
|
|
58
|
+
guard isFinal || !isIncremental else { return nil }
|
|
59
|
+
return _ImageSource(encoded: [UInt8](data))
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public enum CGImageSourceStatus: Int32, Sendable {
|
|
64
|
+
case statusUnexpectedEOF = -5, statusInvalidData = -4, statusUnknownType = -3, statusReadingHeader = -2, statusIncomplete = -1, statusComplete = 0
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public func CGImageSourceCreateWithData(_ data: CFData, _ options: CFDictionary?) -> CGImageSource? {
|
|
68
|
+
let source = CGImageSource(data: data, incremental: false, final: true)
|
|
69
|
+
return source.header == nil ? nil : source
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/// A source over the file at a file URL (the app's own files); nil for
|
|
73
|
+
/// another scheme or a file that is not an image.
|
|
74
|
+
public func CGImageSourceCreateWithURL(_ url: Any, _ options: CFDictionary?) -> CGImageSource? {
|
|
75
|
+
guard let url = url as? URL, url.isFileURL, let data = try? Data(contentsOf: url) else { return nil }
|
|
76
|
+
return CGImageSourceCreateWithData(data, options)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public func CGImageSourceCreateIncremental(_ options: CFDictionary?) -> CGImageSource {
|
|
80
|
+
CGImageSource(data: Data(), incremental: true, final: false)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public func CGImageSourceUpdateData(_ source: CGImageSource, _ data: CFData, _ final: Bool) {
|
|
84
|
+
source.isFinal = final
|
|
85
|
+
source.data = data
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public func CGImageSourceGetStatus(_ source: CGImageSource) -> CGImageSourceStatus {
|
|
89
|
+
guard source.header != nil else { return source.data.isEmpty ? .statusReadingHeader : .statusUnknownType }
|
|
90
|
+
return source.isFinal ? .statusComplete : .statusIncomplete
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public func CGImageSourceGetStatusAtIndex(_ source: CGImageSource, _ index: Int) -> CGImageSourceStatus {
|
|
94
|
+
index >= 0 && index < source.count ? CGImageSourceGetStatus(source) : .statusInvalidData
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// The number of images: a GIF's frames, one for any other file.
|
|
98
|
+
public func CGImageSourceGetCount(_ source: CGImageSource) -> Int { source.count }
|
|
99
|
+
|
|
100
|
+
/// The file's uniform type identifier (`public.png`, `public.jpeg`, `com.compuserve.gif`, …).
|
|
101
|
+
public func CGImageSourceGetType(_ source: CGImageSource) -> CFString? {
|
|
102
|
+
_uniformType(ofMIME: source.header?.mime)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/// The file's size and, for a GIF, its loop count (0: forever; absent when
|
|
106
|
+
/// the file plays once), canvas size and whether it has a global color map.
|
|
107
|
+
public func CGImageSourceCopyProperties(_ source: CGImageSource, _ options: CFDictionary?) -> CFDictionary? {
|
|
108
|
+
guard let header = source.header else { return nil }
|
|
109
|
+
var properties: [String: Any] = [kCGImagePropertyFileSize: source.data.count]
|
|
110
|
+
if let gif = source.gif {
|
|
111
|
+
var dictionary: [String: Any] = [
|
|
112
|
+
kCGImagePropertyGIFHasGlobalColorMap: gif.hasGlobalColorMap,
|
|
113
|
+
kCGImagePropertyGIFCanvasPixelWidth: header.width,
|
|
114
|
+
kCGImagePropertyGIFCanvasPixelHeight: header.height,
|
|
115
|
+
]
|
|
116
|
+
if let loopCount = gif.loopCount { dictionary[kCGImagePropertyGIFLoopCount] = loopCount }
|
|
117
|
+
properties[kCGImagePropertyGIFDictionary] = dictionary
|
|
118
|
+
}
|
|
119
|
+
return properties
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/// The size and orientation (always up: the page applies EXIF rotation) of
|
|
123
|
+
/// image `index`; a GIF frame's delay in seconds, unclamped and as Image I/O
|
|
124
|
+
/// clamps it (a delay under 11 ms is shown for 100 ms, as browsers do).
|
|
125
|
+
public func CGImageSourceCopyPropertiesAtIndex(_ source: CGImageSource, _ index: Int, _ options: CFDictionary?) -> CFDictionary? {
|
|
126
|
+
guard index >= 0, index < source.count, let header = source.header else { return nil }
|
|
127
|
+
var properties: [String: Any] = [
|
|
128
|
+
kCGImagePropertyPixelWidth: header.width,
|
|
129
|
+
kCGImagePropertyPixelHeight: header.height,
|
|
130
|
+
kCGImagePropertyOrientation: UInt32(1),
|
|
131
|
+
]
|
|
132
|
+
if header.mime == "image/jpeg" {
|
|
133
|
+
properties[kCGImagePropertyJFIFDictionary] = [kCGImagePropertyJFIFIsProgressive: _isProgressiveJPEG([UInt8](source.data.prefix(64 * 1024)))] as [String: Any]
|
|
134
|
+
}
|
|
135
|
+
if header.mime == "image/gif" {
|
|
136
|
+
var dictionary = [String: Any]()
|
|
137
|
+
if let frame = source.gif?.frames[safe: index] {
|
|
138
|
+
let unclamped = Double(frame.delay) / 100
|
|
139
|
+
dictionary[kCGImagePropertyGIFUnclampedDelayTime] = unclamped
|
|
140
|
+
dictionary[kCGImagePropertyGIFDelayTime] = unclamped < 0.011 ? 0.1 : unclamped
|
|
141
|
+
}
|
|
142
|
+
properties[kCGImagePropertyGIFDictionary] = dictionary
|
|
143
|
+
}
|
|
144
|
+
return properties
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/// Image `index`: the file, or one frame of an animated GIF as the earlier
|
|
148
|
+
/// frames leave the canvas (the page composes it).
|
|
149
|
+
public func CGImageSourceCreateImageAtIndex(_ source: CGImageSource, _ index: Int, _ options: CFDictionary?) -> CGImage? {
|
|
150
|
+
guard index >= 0, index < source.count, let decoded = source.source else { return nil }
|
|
151
|
+
return CGImage(_recipe: _ImageRecipe(source: decoded, frame: source.isAnimated ? index : nil))
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
extension Array {
|
|
155
|
+
fileprivate subscript(safe index: Int) -> Element? { indices.contains(index) ? self[index] : nil }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/// The image scaled down so its longer side is at most
|
|
159
|
+
/// `kCGImageSourceThumbnailMaxPixelSize` (the whole image without it).
|
|
160
|
+
public func CGImageSourceCreateThumbnailAtIndex(_ source: CGImageSource, _ index: Int, _ options: CFDictionary?) -> CGImage? {
|
|
161
|
+
guard let image = CGImageSourceCreateImageAtIndex(source, index, options) else { return nil }
|
|
162
|
+
let maxSize = (options?[kCGImageSourceThumbnailMaxPixelSize] as? Int).map(Double.init)
|
|
163
|
+
?? (options?[kCGImageSourceThumbnailMaxPixelSize] as? Double)
|
|
164
|
+
?? (options?[kCGImageSourceThumbnailMaxPixelSize] as? CGFloat)
|
|
165
|
+
let longer = Double(max(image.width, image.height))
|
|
166
|
+
guard let maxSize, maxSize > 0, longer > maxSize else { return image }
|
|
167
|
+
let scale = maxSize / longer
|
|
168
|
+
let size = CGSize(width: (Double(image.width) * scale).rounded(), height: (Double(image.height) * scale).rounded())
|
|
169
|
+
guard let ctx = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: 8, bytesPerRow: 0,
|
|
170
|
+
space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else { return image }
|
|
171
|
+
ctx.draw(image, in: CGRect(origin: .zero, size: size))
|
|
172
|
+
return ctx.makeImage()
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/// Whether a JPEG's first frame header is progressive (SOF2).
|
|
176
|
+
func _isProgressiveJPEG(_ bytes: [UInt8]) -> Bool {
|
|
177
|
+
var i = 2
|
|
178
|
+
while i + 3 < bytes.count, bytes[i] == 0xFF {
|
|
179
|
+
let marker = bytes[i + 1]
|
|
180
|
+
if marker == 0xC2 { return true }
|
|
181
|
+
if marker >= 0xC0 && marker <= 0xCF && marker != 0xC4 && marker != 0xC8 && marker != 0xCC { return false }
|
|
182
|
+
let length = Int(bytes[i + 2]) << 8 | Int(bytes[i + 3])
|
|
183
|
+
i += 2 + length
|
|
184
|
+
}
|
|
185
|
+
return false
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// MARK: - Destinations
|
|
189
|
+
|
|
190
|
+
/// Encoding is not available in the browser: no destination can be created.
|
|
191
|
+
public final class CGImageDestination: @unchecked Sendable {}
|
|
192
|
+
|
|
193
|
+
public func CGImageDestinationCreateWithData(_ data: Any, _ type: CFString, _ count: Int, _ options: CFDictionary?) -> CGImageDestination? { nil }
|
|
194
|
+
public func CGImageDestinationAddImage(_ destination: CGImageDestination, _ image: CGImage, _ properties: CFDictionary?) {}
|
|
195
|
+
public func CGImageDestinationFinalize(_ destination: CGImageDestination) -> Bool { false }
|
|
196
|
+
public func CGImageDestinationCopyTypeIdentifiers() -> [String] { [] }
|
|
197
|
+
|
|
198
|
+
// MARK: - Orientation
|
|
199
|
+
|
|
200
|
+
public enum CGImagePropertyOrientation: UInt32, Sendable {
|
|
201
|
+
case up = 1, upMirrored, down, downMirrored, leftMirrored, right, rightMirrored, left
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// MARK: - Keys
|
|
205
|
+
|
|
206
|
+
public let kCGImageSourceTypeIdentifierHint: CFString = "kCGImageSourceTypeIdentifierHint"
|
|
207
|
+
public let kCGImageSourceShouldCache: CFString = "kCGImageSourceShouldCache"
|
|
208
|
+
public let kCGImageSourceShouldCacheImmediately: CFString = "kCGImageSourceShouldCacheImmediately"
|
|
209
|
+
public let kCGImageSourceShouldAllowFloat: CFString = "kCGImageSourceShouldAllowFloat"
|
|
210
|
+
public let kCGImageSourceCreateThumbnailFromImageIfAbsent: CFString = "kCGImageSourceCreateThumbnailFromImageIfAbsent"
|
|
211
|
+
public let kCGImageSourceCreateThumbnailFromImageAlways: CFString = "kCGImageSourceCreateThumbnailFromImageAlways"
|
|
212
|
+
public let kCGImageSourceThumbnailMaxPixelSize: CFString = "kCGImageSourceThumbnailMaxPixelSize"
|
|
213
|
+
public let kCGImageSourceCreateThumbnailWithTransform: CFString = "kCGImageSourceCreateThumbnailWithTransform"
|
|
214
|
+
public let kCGImageSourceSubsampleFactor: CFString = "kCGImageSourceSubsampleFactor"
|
|
215
|
+
|
|
216
|
+
public let kCGImagePropertyFileSize: CFString = "FileSize"
|
|
217
|
+
public let kCGImagePropertyPixelWidth: CFString = "PixelWidth"
|
|
218
|
+
public let kCGImagePropertyPixelHeight: CFString = "PixelHeight"
|
|
219
|
+
public let kCGImagePropertyOrientation: CFString = "Orientation"
|
|
220
|
+
public let kCGImagePropertyDPIWidth: CFString = "DPIWidth"
|
|
221
|
+
public let kCGImagePropertyDPIHeight: CFString = "DPIHeight"
|
|
222
|
+
public let kCGImagePropertyHasAlpha: CFString = "HasAlpha"
|
|
223
|
+
public let kCGImagePropertyColorModel: CFString = "ColorModel"
|
|
224
|
+
public let kCGImagePropertyJFIFDictionary: CFString = "{JFIF}"
|
|
225
|
+
public let kCGImagePropertyJFIFIsProgressive: CFString = "IsProgressive"
|
|
226
|
+
public let kCGImagePropertyGIFDictionary: CFString = "{GIF}"
|
|
227
|
+
public let kCGImagePropertyGIFDelayTime: CFString = "DelayTime"
|
|
228
|
+
public let kCGImagePropertyGIFUnclampedDelayTime: CFString = "UnclampedDelayTime"
|
|
229
|
+
public let kCGImagePropertyGIFLoopCount: CFString = "LoopCount"
|
|
230
|
+
public let kCGImagePropertyGIFHasGlobalColorMap: CFString = "HasGlobalColorMap"
|
|
231
|
+
public let kCGImagePropertyGIFCanvasPixelWidth: CFString = "CanvasPixelWidth"
|
|
232
|
+
public let kCGImagePropertyGIFCanvasPixelHeight: CFString = "CanvasPixelHeight"
|
|
233
|
+
public let kCGImagePropertyPNGDictionary: CFString = "{PNG}"
|
|
234
|
+
public let kCGImagePropertyAPNGDelayTime: CFString = "DelayTime"
|
|
235
|
+
public let kCGImagePropertyAPNGUnclampedDelayTime: CFString = "UnclampedDelayTime"
|
|
236
|
+
public let kCGImagePropertyAPNGLoopCount: CFString = "LoopCount"
|
|
237
|
+
public let kCGImagePropertyWebPDictionary: CFString = "{WebP}"
|
|
238
|
+
public let kCGImagePropertyWebPDelayTime: CFString = "DelayTime"
|
|
239
|
+
public let kCGImagePropertyWebPUnclampedDelayTime: CFString = "UnclampedDelayTime"
|
|
240
|
+
public let kCGImagePropertyWebPLoopCount: CFString = "LoopCount"
|
|
241
|
+
public let kCGImagePropertyHEICSDictionary: CFString = "{HEICS}"
|
|
242
|
+
public let kCGImagePropertyHEICSDelayTime: CFString = "DelayTime"
|
|
243
|
+
public let kCGImagePropertyHEICSUnclampedDelayTime: CFString = "UnclampedDelayTime"
|
|
244
|
+
public let kCGImagePropertyExifDictionary: CFString = "{Exif}"
|
|
245
|
+
public let kCGImagePropertyTIFFDictionary: CFString = "{TIFF}"
|
|
246
|
+
public let kCGImageDestinationLossyCompressionQuality: CFString = "kCGImageDestinationLossyCompressionQuality"
|
|
247
|
+
public let kCGImageDestinationEmbedThumbnail: CFString = "kCGImageDestinationEmbedThumbnail"
|
|
248
|
+
public let kCGImageDestinationImageMaxPixelSize: CFString = "kCGImageDestinationImageMaxPixelSize"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// `CGImage.utType` (iOS 15): the type of the file an image was decoded
|
|
2
|
+
// from. An image straight from a file (an image source's, a `UIImage(data:)`'s)
|
|
3
|
+
// has its file's type; one drawn or filtered since has none, as on iOS.
|
|
4
|
+
|
|
5
|
+
import SwiftUI
|
|
6
|
+
|
|
7
|
+
extension CGImage {
|
|
8
|
+
/// The uniform type identifier of the file the image is (`public.png`,
|
|
9
|
+
/// `public.jpeg`, …); nil once it has been drawn or filtered.
|
|
10
|
+
public var utType: CFString? {
|
|
11
|
+
guard _recipe.steps.isEmpty, let mime = _recipe.source?.mime else { return nil }
|
|
12
|
+
return _uniformType(ofMIME: mime)
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/// The identifier of an image MIME type.
|
|
17
|
+
func _uniformType(ofMIME mime: String?) -> CFString? {
|
|
18
|
+
switch mime {
|
|
19
|
+
case "image/png": "public.png"
|
|
20
|
+
case "image/jpeg": "public.jpeg"
|
|
21
|
+
case "image/gif": "com.compuserve.gif"
|
|
22
|
+
case "image/webp": "org.webmproject.webp"
|
|
23
|
+
case "image/heic": "public.heic"
|
|
24
|
+
case "image/avif": "public.avif"
|
|
25
|
+
case "image/bmp": "com.microsoft.bmp"
|
|
26
|
+
default: nil
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
// `import LinkPresentation` for the browser: link previews.
|
|
2
|
+
//
|
|
3
|
+
// `LPLinkMetadata` (declared in UIKit, which needs it for the share sheet's
|
|
4
|
+
// item sources, and re-exported here) is a plain record of a link's title,
|
|
5
|
+
// URL and pictures; an app that fills it in itself (Ice Cubes' share sheet
|
|
6
|
+
// does) gets what it put there, and the share sheet uses its title.
|
|
7
|
+
//
|
|
8
|
+
// `LPMetadataProvider` fetches the page with the app's `URLSession` (the
|
|
9
|
+
// page's `fetch`) and reads its title and Open Graph tags. A browser only
|
|
10
|
+
// lets a page read another site's HTML when that site allows it (CORS), and
|
|
11
|
+
// most do not, so for most links the fetch fails and the provider reports
|
|
12
|
+
// `LPError.metadataFetchFailed`, as iOS does for a page it cannot load.
|
|
13
|
+
// `LPLinkView` keeps its metadata and draws nothing (docs/ops-protocol.md,
|
|
14
|
+
// "Sharing and media").
|
|
15
|
+
|
|
16
|
+
@_exported import class UIKit.LPLinkMetadata
|
|
17
|
+
import Foundation
|
|
18
|
+
import FoundationBridge
|
|
19
|
+
import SwiftUI
|
|
20
|
+
import UIKit
|
|
21
|
+
|
|
22
|
+
public let LPErrorDomain = "LPErrorDomain"
|
|
23
|
+
|
|
24
|
+
/// Why fetching metadata failed, with LinkPresentation's codes.
|
|
25
|
+
public struct LPError: Error, Hashable, Sendable, CustomNSError {
|
|
26
|
+
public enum Code: Int, Sendable {
|
|
27
|
+
case unknown = 1
|
|
28
|
+
case metadataFetchFailed = 2
|
|
29
|
+
case metadataFetchCancelled = 3
|
|
30
|
+
case metadataFetchTimedOut = 4
|
|
31
|
+
case metadataFetchNotAllowed = 5
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public let code: Code
|
|
35
|
+
public init(_ code: Code) { self.code = code }
|
|
36
|
+
|
|
37
|
+
public static var unknown: Code { .unknown }
|
|
38
|
+
public static var metadataFetchFailed: Code { .metadataFetchFailed }
|
|
39
|
+
public static var metadataFetchCancelled: Code { .metadataFetchCancelled }
|
|
40
|
+
public static var metadataFetchTimedOut: Code { .metadataFetchTimedOut }
|
|
41
|
+
public static var metadataFetchNotAllowed: Code { .metadataFetchNotAllowed }
|
|
42
|
+
|
|
43
|
+
public static var errorDomain: String { LPErrorDomain }
|
|
44
|
+
public var errorCode: Int { code.rawValue }
|
|
45
|
+
public var errorUserInfo: [String: Any] { [:] }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// `catch LPError.metadataFetchFailed`, as with an imported error.
|
|
49
|
+
public func ~= (code: LPError.Code, error: any Error) -> Bool {
|
|
50
|
+
(error as? LPError)?.code == code
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// Fetches a link's metadata: the page's title and Open Graph tags, where
|
|
54
|
+
/// the site lets the browser read it (see the top of the file). One fetch
|
|
55
|
+
/// per provider, as on iOS.
|
|
56
|
+
public final class LPMetadataProvider: @unchecked Sendable {
|
|
57
|
+
/// Whether to load the icon and image too. Kept: the metadata names
|
|
58
|
+
/// them by URL (`imageProvider` loads the picture on request).
|
|
59
|
+
public var shouldFetchSubresources = true
|
|
60
|
+
/// Seconds before the fetch gives up (`metadataFetchTimedOut`).
|
|
61
|
+
public var timeout: TimeInterval = 30
|
|
62
|
+
|
|
63
|
+
private var started = false
|
|
64
|
+
private var cancelled = false
|
|
65
|
+
private var task: Task<Void, Never>?
|
|
66
|
+
|
|
67
|
+
public init() {}
|
|
68
|
+
|
|
69
|
+
public func startFetchingMetadata(for url: URL, completionHandler: @escaping @Sendable (LPLinkMetadata?, (any Error)?) -> Void) {
|
|
70
|
+
guard !started else {
|
|
71
|
+
completionHandler(nil, LPError(.unknown))
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
started = true
|
|
75
|
+
task = Task { @MainActor [self] in
|
|
76
|
+
do {
|
|
77
|
+
let metadata = try await fetch(url)
|
|
78
|
+
completionHandler(cancelled ? nil : metadata, cancelled ? LPError(.metadataFetchCancelled) : nil)
|
|
79
|
+
} catch {
|
|
80
|
+
completionHandler(nil, error)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public func startFetchingMetadata(for url: URL) async throws -> LPLinkMetadata {
|
|
86
|
+
guard !started else { throw LPError(.unknown) }
|
|
87
|
+
started = true
|
|
88
|
+
let metadata = try await fetch(url)
|
|
89
|
+
if cancelled { throw LPError(.metadataFetchCancelled) }
|
|
90
|
+
return metadata
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/// Stops a fetch: its handler hears `metadataFetchCancelled`.
|
|
94
|
+
public func cancel() {
|
|
95
|
+
cancelled = true
|
|
96
|
+
task?.cancel()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@MainActor
|
|
100
|
+
private func fetch(_ url: URL) async throws -> LPLinkMetadata {
|
|
101
|
+
guard let scheme = url.scheme?.lowercased(), scheme == "http" || scheme == "https" else { throw LPError(.metadataFetchNotAllowed) }
|
|
102
|
+
var request = FoundationBridge.URLRequest(url: url)
|
|
103
|
+
request.timeoutInterval = timeout
|
|
104
|
+
let data: Data
|
|
105
|
+
let response: FoundationBridge.URLResponse
|
|
106
|
+
do {
|
|
107
|
+
(data, response) = try await FoundationBridge.URLSession.shared.data(for: request)
|
|
108
|
+
} catch let error as FoundationBridge.URLError where error.code == .timedOut {
|
|
109
|
+
throw LPError(.metadataFetchTimedOut)
|
|
110
|
+
} catch {
|
|
111
|
+
throw LPError(.metadataFetchFailed)
|
|
112
|
+
}
|
|
113
|
+
if let http = response as? FoundationBridge.HTTPURLResponse, !(200..<300).contains(http.statusCode) { throw LPError(.metadataFetchFailed) }
|
|
114
|
+
return Self.metadata(html: String(decoding: data.prefix(512 * 1024), as: UTF8.self), originalURL: url, url: response.url ?? url)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/// The metadata in a page's HTML: `og:title` or `<title>`, `og:image`,
|
|
118
|
+
/// the icon `<link>`, `og:video`; relative URLs resolved against `url`.
|
|
119
|
+
static func metadata(html: String, originalURL: URL, url: URL) -> LPLinkMetadata {
|
|
120
|
+
let metadata = LPLinkMetadata()
|
|
121
|
+
metadata.originalURL = originalURL
|
|
122
|
+
metadata.url = url
|
|
123
|
+
let head = html.range(of: "</head>", options: .caseInsensitive).map { String(html[..<$0.lowerBound]) } ?? html
|
|
124
|
+
let tags = Self.tags(in: head)
|
|
125
|
+
func meta(_ names: String...) -> String? {
|
|
126
|
+
for name in names {
|
|
127
|
+
if let tag = tags.first(where: { $0.name == "meta" && ($0.attributes["property"] ?? $0.attributes["name"])?.lowercased() == name }),
|
|
128
|
+
let content = tag.attributes["content"], !content.isEmpty {
|
|
129
|
+
return content
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return nil
|
|
133
|
+
}
|
|
134
|
+
metadata.title = meta("og:title", "twitter:title") ?? Self.title(in: head)
|
|
135
|
+
if let image = meta("og:image", "og:image:url", "twitter:image"), let imageURL = URL(string: image, relativeTo: url)?.absoluteURL {
|
|
136
|
+
metadata.imageProvider = Self.provider(for: imageURL)
|
|
137
|
+
}
|
|
138
|
+
if let icon = tags.first(where: { $0.name == "link" && ($0.attributes["rel"] ?? "").lowercased().split(separator: " ").contains("icon") }),
|
|
139
|
+
let href = icon.attributes["href"], let iconURL = URL(string: href, relativeTo: url)?.absoluteURL {
|
|
140
|
+
metadata.iconProvider = Self.provider(for: iconURL)
|
|
141
|
+
}
|
|
142
|
+
if let video = meta("og:video", "og:video:url"), let videoURL = URL(string: video, relativeTo: url)?.absoluteURL {
|
|
143
|
+
metadata.remoteVideoURL = videoURL
|
|
144
|
+
}
|
|
145
|
+
return metadata
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/// A provider that downloads the picture at `url` when loaded.
|
|
149
|
+
private static func provider(for url: URL) -> NSItemProvider {
|
|
150
|
+
let provider = NSItemProvider()
|
|
151
|
+
let type = UTType(filenameExtension: url.pathExtension, conformingTo: .image) ?? .image
|
|
152
|
+
provider.registerDataRepresentation(for: type.conforms(to: .image) ? type : .image) { completion in
|
|
153
|
+
Task { @MainActor in
|
|
154
|
+
do { completion(try await FoundationBridge.URLSession.shared.data(from: url).0, nil) } catch { completion(nil, error) }
|
|
155
|
+
}
|
|
156
|
+
return nil
|
|
157
|
+
}
|
|
158
|
+
return provider
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private static func title(in html: String) -> String? {
|
|
162
|
+
guard let open = html.range(of: "<title", options: .caseInsensitive),
|
|
163
|
+
let close = html.range(of: ">", range: open.upperBound..<html.endIndex),
|
|
164
|
+
let end = html.range(of: "</title>", options: .caseInsensitive, range: close.upperBound..<html.endIndex)
|
|
165
|
+
else { return nil }
|
|
166
|
+
let text = decodeEntities(String(html[close.upperBound..<end.lowerBound])).trimmingCharacters(in: .whitespacesAndNewlines)
|
|
167
|
+
return text.isEmpty ? nil : text
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
struct Tag {
|
|
171
|
+
let name: String
|
|
172
|
+
let attributes: [String: String]
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/// The `<meta>` and `<link>` tags, with their attributes (names lower-cased, values decoded).
|
|
176
|
+
static func tags(in html: String) -> [Tag] {
|
|
177
|
+
var tags: [Tag] = []
|
|
178
|
+
var rest = html[...]
|
|
179
|
+
while let open = rest.range(of: "<") {
|
|
180
|
+
rest = rest[open.upperBound...]
|
|
181
|
+
guard let close = rest.range(of: ">") else { break }
|
|
182
|
+
let body = rest[..<close.lowerBound]
|
|
183
|
+
rest = rest[close.upperBound...]
|
|
184
|
+
let name = body.prefix { $0.isLetter }.lowercased()
|
|
185
|
+
guard name == "meta" || name == "link" else { continue }
|
|
186
|
+
tags.append(Tag(name: name, attributes: attributes(String(body.dropFirst(name.count)))))
|
|
187
|
+
}
|
|
188
|
+
return tags
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private static func attributes(_ text: String) -> [String: String] {
|
|
192
|
+
var result: [String: String] = [:]
|
|
193
|
+
var scanner = text[...]
|
|
194
|
+
while true {
|
|
195
|
+
scanner = scanner.drop { $0.isWhitespace || $0 == "/" }
|
|
196
|
+
let key = scanner.prefix { !$0.isWhitespace && $0 != "=" && $0 != "/" }
|
|
197
|
+
guard !key.isEmpty else { break }
|
|
198
|
+
scanner = scanner.dropFirst(key.count).drop { $0.isWhitespace }
|
|
199
|
+
var value = ""
|
|
200
|
+
if scanner.first == "=" {
|
|
201
|
+
scanner = scanner.dropFirst().drop { $0.isWhitespace }
|
|
202
|
+
if let quote = scanner.first, quote == "\"" || quote == "'" {
|
|
203
|
+
let inner = scanner.dropFirst().prefix { $0 != quote }
|
|
204
|
+
value = String(inner)
|
|
205
|
+
scanner = scanner.dropFirst(inner.count + 2)
|
|
206
|
+
} else {
|
|
207
|
+
let bare = scanner.prefix { !$0.isWhitespace }
|
|
208
|
+
value = String(bare)
|
|
209
|
+
scanner = scanner.dropFirst(bare.count)
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
result[key.lowercased()] = decodeEntities(value)
|
|
213
|
+
}
|
|
214
|
+
return result
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
private static func decodeEntities(_ text: String) -> String {
|
|
218
|
+
guard text.contains("&") else { return text }
|
|
219
|
+
var result = text
|
|
220
|
+
for (entity, character) in [("&", "&"), ("<", "<"), (">", ">"), (""", "\""), ("'", "'"), ("'", "'"), ("'", "'")] {
|
|
221
|
+
result = result.replacingOccurrences(of: entity, with: character)
|
|
222
|
+
}
|
|
223
|
+
return result
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/// A rich link preview. Kept with its metadata; it draws nothing (no
|
|
228
|
+
/// representable translates it).
|
|
229
|
+
@MainActor
|
|
230
|
+
open class LPLinkView: UIView {
|
|
231
|
+
open var metadata: LPLinkMetadata
|
|
232
|
+
|
|
233
|
+
public init(metadata: LPLinkMetadata) {
|
|
234
|
+
self.metadata = metadata
|
|
235
|
+
super.init(frame: .zero)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
public convenience init(url: URL) {
|
|
239
|
+
let metadata = LPLinkMetadata()
|
|
240
|
+
metadata.originalURL = url
|
|
241
|
+
metadata.url = url
|
|
242
|
+
self.init(metadata: metadata)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
public required init?(coder: NSCoder) {
|
|
246
|
+
metadata = LPLinkMetadata()
|
|
247
|
+
super.init(coder: coder)
|
|
248
|
+
}
|
|
249
|
+
}
|