@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,386 @@
|
|
|
1
|
+
// `Text`: a string, or styled runs of strings and images.
|
|
2
|
+
//
|
|
3
|
+
// A `Text` is a list of segments, each with its own style. `Text + Text`
|
|
4
|
+
// joins segments; the modifiers that return `Text` (`bold()`, `italic()`,
|
|
5
|
+
// `font(_:)`, `foregroundColor(_:)`, `strikethrough()`, `underline()`,
|
|
6
|
+
// `kerning(_:)`, `baselineOffset(_:)`…) style every segment, and the style a
|
|
7
|
+
// segment already has wins (as in SwiftUI, an inner modifier overrides an
|
|
8
|
+
// outer one). An image interpolated into a `LocalizedStringKey`
|
|
9
|
+
// (`Text("\(Image(systemName: "star")) Favorites")`) is a segment of its own.
|
|
10
|
+
//
|
|
11
|
+
// What reaches the page depends on what the text needs:
|
|
12
|
+
//
|
|
13
|
+
// - A single string styled only by `font`, `fontWeight`, `bold` and the
|
|
14
|
+
// foreground is the `text` element it always was, inside the same `styled`
|
|
15
|
+
// elements the View modifiers make (so existing apps render unchanged).
|
|
16
|
+
// - Anything else (several segments, images, links, italic, strikethrough,
|
|
17
|
+
// underline, kerning, baseline offsets) is one `text` element whose `runs`
|
|
18
|
+
// prop lists each segment with its style; the page lays the runs out as one
|
|
19
|
+
// paragraph (docs/ops-protocol.md, "Rich text").
|
|
20
|
+
//
|
|
21
|
+
// A run with a link sends a `link` event when tapped; the environment's link
|
|
22
|
+
// handler opens it (FoundationBridge's `openURL` sets it).
|
|
23
|
+
|
|
24
|
+
import Foundation
|
|
25
|
+
|
|
26
|
+
public struct Text: Equatable, Sendable {
|
|
27
|
+
/// The whole text as a string (images contribute nothing).
|
|
28
|
+
let content: String
|
|
29
|
+
let segments: [_TextSegment]
|
|
30
|
+
/// The modifiers applied to the whole text, in order, while it is one
|
|
31
|
+
/// plain string: they become the View modifiers they always were.
|
|
32
|
+
let applied: [_TextModifier]
|
|
33
|
+
|
|
34
|
+
init(segments: [_TextSegment], applied: [_TextModifier] = []) {
|
|
35
|
+
self.segments = segments
|
|
36
|
+
self.applied = applied
|
|
37
|
+
content = segments.map { if case .string(let s) = $0.content { s } else { "" } }.joined()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public init(verbatim content: String) {
|
|
41
|
+
self.init(segments: [_TextSegment(content: .string(content))])
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public init(_ key: LocalizedStringKey) {
|
|
45
|
+
self.init(segments: key.segments)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// A key from a table other than `Localizable`, or another bundle's
|
|
49
|
+
/// (FoundationBridge's `Text(_:tableName:bundle:comment:)` passes its path).
|
|
50
|
+
public init(_ key: LocalizedStringKey, _table table: String?, bundlePath: String?) {
|
|
51
|
+
self.init(segments: key._segments(table: table, bundlePath: bundlePath))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@_disfavoredOverload
|
|
55
|
+
public init<S: StringProtocol>(_ content: S) {
|
|
56
|
+
self.init(verbatim: String(content))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/// `Text(price, format: .currency(code: "USD"))`, `Text(date, format: .dateTime.year())`.
|
|
60
|
+
public init<F: FormatStyle>(_ input: F.FormatInput, format: F) where F.FormatOutput == String {
|
|
61
|
+
self.init(verbatim: format.format(input))
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/// An image inline in text (`Text(Image(systemName: "star"))`).
|
|
65
|
+
public init(_ image: Image) {
|
|
66
|
+
self.init(segments: [_TextSegment(content: .image(image))])
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/// The text as a string, for bridge modules (a `prompt` becomes a placeholder).
|
|
70
|
+
public var _content: String { content }
|
|
71
|
+
|
|
72
|
+
public static func + (lhs: Text, rhs: Text) -> Text {
|
|
73
|
+
Text(segments: lhs.segments + rhs.segments)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/// Whether the page gets the plain `text` element the shim has always made.
|
|
77
|
+
var isPlain: Bool {
|
|
78
|
+
guard segments.count == 1, case .string = segments[0].content else { return false }
|
|
79
|
+
return applied.allSatisfy(\.isLegacy) && segments[0].style.isLegacyOnly
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/// Applies `modifier` to every segment where it is not set yet.
|
|
83
|
+
func modified(_ modifier: _TextModifier) -> Text {
|
|
84
|
+
Text(segments: segments.map { segment in
|
|
85
|
+
var segment = segment
|
|
86
|
+
modifier.apply(to: &segment.style)
|
|
87
|
+
return segment
|
|
88
|
+
}, applied: applied + [modifier])
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// MARK: Styling
|
|
92
|
+
|
|
93
|
+
public func font(_ font: Font?) -> Text { modified(.font(font)) }
|
|
94
|
+
public func fontWeight(_ weight: Font.Weight?) -> Text { modified(.weight(weight)) }
|
|
95
|
+
public func bold() -> Text { modified(.weight(.bold)) }
|
|
96
|
+
public func bold(_ isActive: Bool) -> Text { isActive ? bold() : self }
|
|
97
|
+
public func italic() -> Text { modified(.italic) }
|
|
98
|
+
public func italic(_ isActive: Bool) -> Text { isActive ? italic() : self }
|
|
99
|
+
public func monospaced(_ isActive: Bool = true) -> Text { isActive ? modified(.monospaced) : self }
|
|
100
|
+
public func monospacedDigit() -> Text { self }
|
|
101
|
+
public func fontDesign(_ design: Font.Design?) -> Text { design == .monospaced ? modified(.monospaced) : self }
|
|
102
|
+
public func fontWidth(_ width: Font.Width?) -> Text { self }
|
|
103
|
+
public func foregroundColor(_ color: Color?) -> Text { modified(.foreground(color?._json)) }
|
|
104
|
+
public func foregroundStyle<S: ShapeStyle>(_ style: S) -> Text { modified(.foreground(style._json)) }
|
|
105
|
+
public func strikethrough(_ isActive: Bool = true, color: Color? = nil) -> Text {
|
|
106
|
+
isActive ? modified(.strikethrough(color?._json ?? .null)) : self
|
|
107
|
+
}
|
|
108
|
+
public func strikethrough(_ isActive: Bool = true, pattern: Text.LineStyle.Pattern, color: Color? = nil) -> Text {
|
|
109
|
+
strikethrough(isActive, color: color)
|
|
110
|
+
}
|
|
111
|
+
public func underline(_ isActive: Bool = true, color: Color? = nil) -> Text {
|
|
112
|
+
isActive ? modified(.underline(color?._json ?? .null)) : self
|
|
113
|
+
}
|
|
114
|
+
public func underline(_ isActive: Bool = true, pattern: Text.LineStyle.Pattern, color: Color? = nil) -> Text {
|
|
115
|
+
underline(isActive, color: color)
|
|
116
|
+
}
|
|
117
|
+
public func kerning(_ kerning: CGFloat) -> Text { modified(.kerning(kerning)) }
|
|
118
|
+
public func tracking(_ tracking: CGFloat) -> Text { modified(.kerning(tracking)) }
|
|
119
|
+
public func baselineOffset(_ offset: CGFloat) -> Text { modified(.baselineOffset(offset)) }
|
|
120
|
+
public func textScale(_ scale: Text.Scale, isEnabled: Bool = true) -> Text { self }
|
|
121
|
+
public func speechSpellsOutCharacters(_ value: Bool = true) -> Text { self }
|
|
122
|
+
public func accessibilityLabel(_ label: Text) -> Text { self }
|
|
123
|
+
@_disfavoredOverload
|
|
124
|
+
public func accessibilityLabel<S: StringProtocol>(_ label: S) -> Text { self }
|
|
125
|
+
public func accessibilityLabel(_ labelKey: LocalizedStringKey) -> Text { self }
|
|
126
|
+
|
|
127
|
+
/// The segments open `url` when tapped (Foundation's `Text(AttributedString)` uses it).
|
|
128
|
+
public func _link(_ url: String) -> Text { modified(.link(url)) }
|
|
129
|
+
|
|
130
|
+
public struct LineStyle: Hashable, Sendable {
|
|
131
|
+
public struct Pattern: Hashable, Sendable {
|
|
132
|
+
let name: String
|
|
133
|
+
public static let solid = Pattern(name: "solid")
|
|
134
|
+
public static let dot = Pattern(name: "dot")
|
|
135
|
+
public static let dash = Pattern(name: "dash")
|
|
136
|
+
public static let dashDot = Pattern(name: "dashDot")
|
|
137
|
+
public static let dashDotDot = Pattern(name: "dashDotDot")
|
|
138
|
+
}
|
|
139
|
+
public init(pattern: Pattern = .solid, color: Color? = nil) {}
|
|
140
|
+
public static let single = LineStyle()
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
public enum Scale: Hashable, Sendable { case `default`, secondary }
|
|
144
|
+
|
|
145
|
+
public enum Case: Hashable, Sendable { case uppercase, lowercase }
|
|
146
|
+
|
|
147
|
+
public enum TruncationMode: Hashable, Sendable { case head, tail, middle }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
extension Font {
|
|
151
|
+
public struct Width: Hashable, Sendable {
|
|
152
|
+
let value: CGFloat
|
|
153
|
+
public init(_ value: CGFloat) { self.value = value }
|
|
154
|
+
public static let compressed = Width(-0.3)
|
|
155
|
+
public static let condensed = Width(-0.2)
|
|
156
|
+
public static let standard = Width(0)
|
|
157
|
+
public static let expanded = Width(0.2)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/// One stretch of a `Text`: a string or an image, with its style.
|
|
162
|
+
struct _TextSegment: Equatable, Sendable {
|
|
163
|
+
enum Content: Equatable, Sendable {
|
|
164
|
+
case string(String)
|
|
165
|
+
case image(Image)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
var content: Content
|
|
169
|
+
var style = _TextStyle()
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/// The style of a segment; nil fields are unset (an outer modifier may set them).
|
|
173
|
+
struct _TextStyle: Equatable, Sendable {
|
|
174
|
+
var font: Font?
|
|
175
|
+
var weight: Font.Weight?
|
|
176
|
+
var italic = false
|
|
177
|
+
var monospaced = false
|
|
178
|
+
var foreground: _JSON?
|
|
179
|
+
/// The line's color, `.null` for the text's own.
|
|
180
|
+
var strikethrough: _JSON?
|
|
181
|
+
var underline: _JSON?
|
|
182
|
+
var kerning: CGFloat?
|
|
183
|
+
var baselineOffset: CGFloat?
|
|
184
|
+
var link: String?
|
|
185
|
+
|
|
186
|
+
/// Only what the View modifiers express (font, weight, foreground).
|
|
187
|
+
var isLegacyOnly: Bool {
|
|
188
|
+
!italic && !monospaced && strikethrough == nil && underline == nil && kerning == nil && baselineOffset == nil && link == nil
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/// The run's style as the page reads it.
|
|
192
|
+
var json: [String: _JSON] {
|
|
193
|
+
var object: [String: _JSON] = [:]
|
|
194
|
+
if font != nil || weight != nil || monospaced {
|
|
195
|
+
var resolved = font ?? Font(size: nil, weight: nil, design: nil)
|
|
196
|
+
if let weight { resolved.weight = weight }
|
|
197
|
+
if monospaced { resolved.design = .monospaced }
|
|
198
|
+
object["font"] = resolved._json
|
|
199
|
+
}
|
|
200
|
+
if italic { object["italic"] = .bool(true) }
|
|
201
|
+
if let foreground { object["foreground"] = foreground }
|
|
202
|
+
if let strikethrough { object["strikethrough"] = .object(strikethrough == .null ? [:] : ["color": strikethrough]) }
|
|
203
|
+
if let underline { object["underline"] = .object(underline == .null ? [:] : ["color": underline]) }
|
|
204
|
+
if let kerning { object["kerning"] = .number(kerning) }
|
|
205
|
+
if let baselineOffset { object["baselineOffset"] = .number(baselineOffset) }
|
|
206
|
+
if let link { object["link"] = .string(link) }
|
|
207
|
+
return object
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/// A modifier that returns `Text`, kept to style later segments and, for a
|
|
212
|
+
/// plain text, to become the View modifier it always was.
|
|
213
|
+
enum _TextModifier: Equatable, Sendable {
|
|
214
|
+
case font(Font?)
|
|
215
|
+
case weight(Font.Weight?)
|
|
216
|
+
case italic
|
|
217
|
+
case monospaced
|
|
218
|
+
case foreground(_JSON?)
|
|
219
|
+
case strikethrough(_JSON)
|
|
220
|
+
case underline(_JSON)
|
|
221
|
+
case kerning(CGFloat)
|
|
222
|
+
case baselineOffset(CGFloat)
|
|
223
|
+
case link(String)
|
|
224
|
+
|
|
225
|
+
var isLegacy: Bool {
|
|
226
|
+
switch self {
|
|
227
|
+
case .font, .weight, .foreground: true
|
|
228
|
+
default: false
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
func apply(to style: inout _TextStyle) {
|
|
233
|
+
switch self {
|
|
234
|
+
case .font(let font): if style.font == nil { style.font = font }
|
|
235
|
+
case .weight(let weight): if style.weight == nil { style.weight = weight }
|
|
236
|
+
case .italic: style.italic = true
|
|
237
|
+
case .monospaced: style.monospaced = true
|
|
238
|
+
case .foreground(let paint): if style.foreground == nil { style.foreground = paint }
|
|
239
|
+
case .strikethrough(let color): if style.strikethrough == nil { style.strikethrough = color }
|
|
240
|
+
case .underline(let color): if style.underline == nil { style.underline = color }
|
|
241
|
+
case .kerning(let value): if style.kerning == nil { style.kerning = value }
|
|
242
|
+
case .baselineOffset(let value): if style.baselineOffset == nil { style.baselineOffset = value }
|
|
243
|
+
case .link(let url): if style.link == nil { style.link = url }
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
extension Text: _PrimitiveView {}
|
|
249
|
+
extension Text: _GroupView {
|
|
250
|
+
var _children: [any View] {
|
|
251
|
+
guard isPlain, case .string(let string) = segments[0].content else { return [_RichText(text: self)] }
|
|
252
|
+
var view: any View = _PlainText(content: string)
|
|
253
|
+
for modifier in applied {
|
|
254
|
+
switch modifier {
|
|
255
|
+
case .font(let font): view = _restyle(view) { $0.font = font }
|
|
256
|
+
case .weight(let weight): view = _restyle(view) { $0.font = weight.map { Font(size: nil, weight: $0, design: nil) } }
|
|
257
|
+
case .foreground(let paint): view = _restyle(view) { $0.foreground = paint.map(_JSONShapeStyle.init) }
|
|
258
|
+
default: break
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return [view]
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/// `view` in a `styled` element, as the View modifiers wrap it.
|
|
266
|
+
private func _restyle(_ view: any View, _ change: (inout _Style) -> Void) -> any View {
|
|
267
|
+
var style = _Style()
|
|
268
|
+
change(&style)
|
|
269
|
+
func open<V: View>(_ view: V) -> any View { _StyledGroup(content: view, style: style) }
|
|
270
|
+
return open(view)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/// A paint the page already has as JSON (a `Text`'s foreground).
|
|
274
|
+
struct _JSONShapeStyle: ShapeStyle {
|
|
275
|
+
let json: _JSON
|
|
276
|
+
init(_ json: _JSON) { self.json = json }
|
|
277
|
+
var _json: _JSON { json }
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/// The page's `text` element for a plain string.
|
|
281
|
+
struct _PlainText: Equatable, Sendable {
|
|
282
|
+
let content: String
|
|
283
|
+
}
|
|
284
|
+
extension _PlainText: _PrimitiveView {}
|
|
285
|
+
extension _PlainText: _HostView {
|
|
286
|
+
var _kind: String { "text" }
|
|
287
|
+
func _props(in environment: EnvironmentValues) -> _JSON {
|
|
288
|
+
.object(["text": .string(environment.textCase?.apply(to: content) ?? content)])
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/// The page's `text` element with runs.
|
|
293
|
+
struct _RichText: Equatable, Sendable {
|
|
294
|
+
let text: Text
|
|
295
|
+
|
|
296
|
+
var hasLinks: Bool { text.segments.contains { $0.style.link != nil } }
|
|
297
|
+
|
|
298
|
+
/// Sends inline bitmaps' sources to the page before the element.
|
|
299
|
+
@MainActor func _sendBitmapSources(through runtime: _Runtime) {
|
|
300
|
+
for segment in text.segments {
|
|
301
|
+
if case .image(let image) = segment.content { image._sendBitmapSource(through: runtime) }
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
extension _RichText: _PrimitiveView {}
|
|
306
|
+
extension _RichText: _HostView {
|
|
307
|
+
var _kind: String { "text" }
|
|
308
|
+
func _props(in environment: EnvironmentValues) -> _JSON {
|
|
309
|
+
let runs: [_JSON] = text.segments.map { segment in
|
|
310
|
+
var run = segment.style.json
|
|
311
|
+
switch segment.content {
|
|
312
|
+
case .string(let string): run["text"] = .string(environment.textCase?.apply(to: string) ?? string)
|
|
313
|
+
case .image(let image): run["image"] = image._props(in: environment)
|
|
314
|
+
}
|
|
315
|
+
return .object(run)
|
|
316
|
+
}
|
|
317
|
+
let content = environment.textCase?.apply(to: text.content) ?? text.content
|
|
318
|
+
return .object(["text": .string(content), "runs": .array(runs)])
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
extension _RichText: _EnvironmentHandling {
|
|
322
|
+
/// A tap on a link run: the page sends its URL.
|
|
323
|
+
func _handler(in environment: EnvironmentValues) -> ((_JSON) -> Void)? {
|
|
324
|
+
guard hasLinks else { return nil }
|
|
325
|
+
let open = environment._openLink
|
|
326
|
+
return { payload in
|
|
327
|
+
guard case .string(let url) = payload else { return }
|
|
328
|
+
MainActor.assumeIsolated { (open ?? _Runtime.shared._openLinkOnPage)(url) }
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// MARK: - Interpolated text
|
|
334
|
+
|
|
335
|
+
extension LocalizedStringKey {
|
|
336
|
+
/// The localized text as segments: its strings, and the images and texts
|
|
337
|
+
/// interpolated into it, where the localized format puts them.
|
|
338
|
+
var segments: [_TextSegment] { _segments(table: nil, bundlePath: nil) }
|
|
339
|
+
|
|
340
|
+
func _segments(table: String?, bundlePath: String?) -> [_TextSegment] {
|
|
341
|
+
var out: [_TextSegment] = []
|
|
342
|
+
for piece in _pieces(table: table, bundlePath: bundlePath) {
|
|
343
|
+
switch piece {
|
|
344
|
+
case .text(let string):
|
|
345
|
+
if case .string(let previous)? = out.last?.content, out.last?.style == _TextStyle() {
|
|
346
|
+
out[out.count - 1].content = .string(previous + string)
|
|
347
|
+
} else {
|
|
348
|
+
out.append(_TextSegment(content: .string(string)))
|
|
349
|
+
}
|
|
350
|
+
case .argument(let index):
|
|
351
|
+
guard index < arguments.count else { continue }
|
|
352
|
+
switch arguments[index] {
|
|
353
|
+
case .image(let image): out.append(_TextSegment(content: .image(image)))
|
|
354
|
+
case .text(let text): out += text.segments
|
|
355
|
+
case .value: continue
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
if out.isEmpty { out.append(_TextSegment(content: .string(""))) }
|
|
360
|
+
return out
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// MARK: - Links
|
|
365
|
+
|
|
366
|
+
private struct _OpenLinkKey: EnvironmentKey {
|
|
367
|
+
static var defaultValue: (@MainActor @Sendable (String) -> Void)? { nil }
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
extension EnvironmentValues {
|
|
371
|
+
/// What a tap on a link in a `Text` calls; FoundationBridge's `openURL`
|
|
372
|
+
/// sets it. Without one, the page opens the link.
|
|
373
|
+
public var _openLink: (@MainActor @Sendable (String) -> Void)? {
|
|
374
|
+
get { self[_OpenLinkKey.self] }
|
|
375
|
+
set { self[_OpenLinkKey.self] = newValue }
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
extension _Runtime {
|
|
380
|
+
/// Asks the page to open `url` (in a new tab; headless runs log the request).
|
|
381
|
+
public func _openLinkOnPage(_ url: String) {
|
|
382
|
+
Task { @MainActor in
|
|
383
|
+
_ = try? await _hostRequest(kind: "openURL", params: .object(["url": .string(url)]))
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
// `Text(date, style:)` and `Text(range)` / `Text(interval)`. The date is
|
|
4
|
+
// formatted when the view's body runs, in the viewer's locale, and the text
|
|
5
|
+
// does not update by itself afterwards (on iOS `.relative`, `.offset` and
|
|
6
|
+
// `.timer` count live): an approximation that is exact for `.date` and
|
|
7
|
+
// `.time`, and right until the next render for the others.
|
|
8
|
+
|
|
9
|
+
extension Text {
|
|
10
|
+
public struct DateStyle: Hashable, Sendable {
|
|
11
|
+
let name: String
|
|
12
|
+
/// "June 3, 2024".
|
|
13
|
+
public static let date = DateStyle(name: "date")
|
|
14
|
+
/// "3:04 PM".
|
|
15
|
+
public static let time = DateStyle(name: "time")
|
|
16
|
+
/// "2 hr, 5 min" (from now, either direction).
|
|
17
|
+
public static let relative = DateStyle(name: "relative")
|
|
18
|
+
/// "+2 hr, 5 min" / "-2 hr, 5 min".
|
|
19
|
+
public static let offset = DateStyle(name: "offset")
|
|
20
|
+
/// "2:05:00", the time from or since the date.
|
|
21
|
+
public static let timer = DateStyle(name: "timer")
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public init(_ date: Date, style: DateStyle) {
|
|
25
|
+
self.init(verbatim: Text._format(date, style: style, now: Date()))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public init(_ dates: ClosedRange<Date>) {
|
|
29
|
+
let format = Date.FormatStyle(date: .abbreviated, time: .shortened)
|
|
30
|
+
self.init(verbatim: "\(dates.lowerBound.formatted(format)) – \(dates.upperBound.formatted(format))")
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public init(_ interval: DateInterval) {
|
|
34
|
+
self.init(interval.start...interval.end)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static func _format(_ date: Date, style: DateStyle, now: Date) -> String {
|
|
38
|
+
switch style.name {
|
|
39
|
+
case "date":
|
|
40
|
+
return date.formatted(date: .long, time: .omitted)
|
|
41
|
+
case "time":
|
|
42
|
+
return date.formatted(date: .omitted, time: .shortened)
|
|
43
|
+
case "relative", "offset":
|
|
44
|
+
let seconds = Int(abs(date.timeIntervalSince(now)).rounded())
|
|
45
|
+
let hours = seconds / 3600, minutes = seconds % 3600 / 60, rest = seconds % 60
|
|
46
|
+
var parts: [String] = []
|
|
47
|
+
if hours > 0 { parts.append("\(hours) hr") }
|
|
48
|
+
if minutes > 0 { parts.append("\(minutes) min") }
|
|
49
|
+
if hours == 0 && (rest > 0 || parts.isEmpty) { parts.append("\(rest) sec") }
|
|
50
|
+
let amount = parts.prefix(2).joined(separator: ", ")
|
|
51
|
+
if style.name == "relative" { return amount }
|
|
52
|
+
return (date >= now ? "+" : "-") + amount
|
|
53
|
+
default:
|
|
54
|
+
let seconds = Int(abs(date.timeIntervalSince(now)))
|
|
55
|
+
let hours = seconds / 3600, minutes = seconds % 3600 / 60, rest = seconds % 60
|
|
56
|
+
let mm = minutes < 10 && hours > 0 ? "0\(minutes)" : "\(minutes)"
|
|
57
|
+
let ss = rest < 10 ? "0\(rest)" : "\(rest)"
|
|
58
|
+
return hours > 0 ? "\(hours):\(mm):\(ss)" : "\(mm):\(ss)"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/// Text modifiers on any view (iOS 16): `.underline()`, `.strikethrough()`
|
|
2
|
+
/// and `.italic()` style every text inside. Each is a style key of a `styled`
|
|
3
|
+
/// box (`underline`, `strikethrough`, `italic`: true, or false to turn it off
|
|
4
|
+
/// below an ancestor that set it), which the page passes down to the texts
|
|
5
|
+
/// below through inherited CSS (Web/src/renderer.ts `applyStyle`). The line
|
|
6
|
+
/// takes the text's color: `pattern` and `color` are not drawn. Italic text is
|
|
7
|
+
/// measured upright.
|
|
8
|
+
///
|
|
9
|
+
/// `.fontDesign` and `.monospaced` set the design of the fonts inside, as
|
|
10
|
+
/// `.fontWeight` sets their weight. `.monospacedDigit`, `.fontWidth`,
|
|
11
|
+
/// `.minimumScaleFactor` and `.allowsTightening` are accepted: digits keep the
|
|
12
|
+
/// font's own widths, and the page wraps or truncates text at its size
|
|
13
|
+
/// instead of shrinking it.
|
|
14
|
+
|
|
15
|
+
nonisolated extension View {
|
|
16
|
+
/// Underlines the texts inside.
|
|
17
|
+
public func underline(_ isActive: Bool = true, pattern: Text.LineStyle.Pattern = .solid, color: Color? = nil) -> some View {
|
|
18
|
+
_styled { $0.extras["underline"] = .bool(isActive) }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// Strikes the texts inside through.
|
|
22
|
+
public func strikethrough(_ isActive: Bool = true, pattern: Text.LineStyle.Pattern = .solid, color: Color? = nil) -> some View {
|
|
23
|
+
_styled { $0.extras["strikethrough"] = .bool(isActive) }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/// Slants the texts inside.
|
|
27
|
+
public func italic(_ isActive: Bool = true) -> some View {
|
|
28
|
+
_styled { $0.extras["italic"] = .bool(isActive) }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/// The design of the fonts inside (nil leaves it as it is).
|
|
32
|
+
public func fontDesign(_ design: Font.Design?) -> some View {
|
|
33
|
+
_styled { $0.font = design.map { Font(size: nil, weight: nil, design: $0) } }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// Monospaced fonts inside.
|
|
37
|
+
public func monospaced(_ isActive: Bool = true) -> some View {
|
|
38
|
+
_styled { $0.font = isActive ? Font(size: nil, weight: nil, design: .monospaced) : nil }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// Accepted: digits keep their proportional widths.
|
|
42
|
+
public func monospacedDigit() -> some View { self }
|
|
43
|
+
|
|
44
|
+
/// Accepted: the page has one width of each font.
|
|
45
|
+
public func fontWidth(_ width: Font.Width?) -> some View { self }
|
|
46
|
+
|
|
47
|
+
/// Accepted: text is not shrunk to fit.
|
|
48
|
+
public func minimumScaleFactor(_ factor: CGFloat) -> some View { self }
|
|
49
|
+
|
|
50
|
+
/// Accepted: letter spacing is not tightened to fit.
|
|
51
|
+
public func allowsTightening(_ flag: Bool) -> some View { self }
|
|
52
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// Multi-line text fields (`TextField(_:text:axis: .vertical)`), line-limit
|
|
2
|
+
// ranges, and `.textCase(_:)`.
|
|
3
|
+
//
|
|
4
|
+
// A vertical field is the `textfield` element with `"axis": "vertical"`:
|
|
5
|
+
// the page draws a `<textarea>` that grows with its text from the lower
|
|
6
|
+
// bound of the enclosing line-limit range to its upper bound (one line to
|
|
7
|
+
// unlimited by default) and scrolls inside past that. Typing sends `text`
|
|
8
|
+
// events as for a one-line field; Return inserts a line break, except in a
|
|
9
|
+
// field with `.onSubmit`, where Return submits and Shift-Return breaks the
|
|
10
|
+
// line (iOS has no Shift, and submits).
|
|
11
|
+
//
|
|
12
|
+
// `.lineLimit(2...4)` / `.lineLimit(...4)` / `.lineLimit(2...)` set the
|
|
13
|
+
// style's `lineLimit` to the upper bound (null for none) and, when the lower
|
|
14
|
+
// bound is above one, `lineLimitMin`. Text takes the lines it needs up to the
|
|
15
|
+
// upper bound (as with `reservesSpace`, the lower bound reserves nothing for
|
|
16
|
+
// text); a vertical field is at least `lineLimitMin` lines tall.
|
|
17
|
+
|
|
18
|
+
extension TextField where Label == Text {
|
|
19
|
+
@_disfavoredOverload
|
|
20
|
+
public init<S: StringProtocol>(_ title: S, text: Binding<String>, axis: Axis) {
|
|
21
|
+
self.init(placeholder: String(title), text: text, secure: false)
|
|
22
|
+
self.axis = axis
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@_disfavoredOverload
|
|
26
|
+
public init<S: StringProtocol>(_ title: S, text: Binding<String>, prompt: Text?, axis: Axis) {
|
|
27
|
+
self.init(placeholder: prompt?.content ?? String(title), text: text, secure: false)
|
|
28
|
+
self.axis = axis
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public init(_ titleKey: LocalizedStringKey, text: Binding<String>, axis: Axis) {
|
|
32
|
+
self.init(placeholder: titleKey.key, text: text, secure: false)
|
|
33
|
+
self.axis = axis
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public init(_ titleKey: LocalizedStringKey, text: Binding<String>, prompt: Text?, axis: Axis) {
|
|
37
|
+
self.init(placeholder: prompt?.content ?? titleKey.key, text: text, secure: false)
|
|
38
|
+
self.axis = axis
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
extension TextField {
|
|
43
|
+
/// A field labelled by a view: the prompt (or else the label's text) is the placeholder.
|
|
44
|
+
@MainActor
|
|
45
|
+
public init(text: Binding<String>, prompt: Text? = nil, axis: Axis, @ViewBuilder label: () -> Label) {
|
|
46
|
+
self.init(placeholder: prompt?.content ?? _textContent(of: label()), text: text, secure: false)
|
|
47
|
+
self.axis = axis
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
nonisolated extension View {
|
|
52
|
+
/// Text takes up to `limit.upperBound` lines; a vertical text field is
|
|
53
|
+
/// `limit.lowerBound` to `limit.upperBound` lines tall.
|
|
54
|
+
public func lineLimit(_ limit: ClosedRange<Int>) -> some View {
|
|
55
|
+
_lineLimit(min: limit.lowerBound, max: limit.upperBound)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public func lineLimit(_ limit: PartialRangeThrough<Int>) -> some View {
|
|
59
|
+
_lineLimit(min: 1, max: limit.upperBound)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public func lineLimit(_ limit: PartialRangeFrom<Int>) -> some View {
|
|
63
|
+
_lineLimit(min: limit.lowerBound, max: nil)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
func _lineLimit(min: Int, max: Int?) -> some View {
|
|
67
|
+
_styled { style in
|
|
68
|
+
style.lineLimit = max ?? -1
|
|
69
|
+
if min > 1 { style.extras["lineLimitMin"] = .number(Double(min)) }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// MARK: - textCase
|
|
75
|
+
|
|
76
|
+
enum _TextCaseKey: EnvironmentKey { static let defaultValue: Text.Case? = nil }
|
|
77
|
+
|
|
78
|
+
extension EnvironmentValues {
|
|
79
|
+
/// `.textCase(_:)`: texts below are upper- or lower-cased when emitted
|
|
80
|
+
/// (so the page measures and draws the cased string); nil leaves them.
|
|
81
|
+
public var textCase: Text.Case? {
|
|
82
|
+
get { self[_TextCaseKey.self] }
|
|
83
|
+
set { self[_TextCaseKey.self] = newValue }
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
nonisolated extension View {
|
|
88
|
+
/// Upper- or lower-cases the texts in this view (`nil`: as written, which
|
|
89
|
+
/// also undoes the upper case of a list section header).
|
|
90
|
+
public func textCase(_ textCase: Text.Case?) -> some View {
|
|
91
|
+
environment(\.textCase, textCase)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
extension Text.Case {
|
|
96
|
+
/// `string` in this case.
|
|
97
|
+
func apply(to string: String) -> String {
|
|
98
|
+
switch self {
|
|
99
|
+
case .uppercase: string.uppercased()
|
|
100
|
+
case .lowercase: string.lowercased()
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/// iOS 13's text field initializers with `onEditingChanged:` and `onCommit:`.
|
|
2
|
+
/// `onCommit` runs when the viewer presses Return, as `.onSubmit` does (the
|
|
3
|
+
/// field asks the page for submit events). `onEditingChanged` is accepted and
|
|
4
|
+
/// not called: use `.focused` to follow editing.
|
|
5
|
+
extension TextField where Label == Text {
|
|
6
|
+
public init(_ titleKey: LocalizedStringKey, text: Binding<String>,
|
|
7
|
+
onEditingChanged: @escaping (Bool) -> Void = { _ in }, onCommit: @escaping () -> Void) {
|
|
8
|
+
self.init(placeholder: titleKey.key, text: text, secure: false)
|
|
9
|
+
self.onCommit = onCommit
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@_disfavoredOverload
|
|
13
|
+
public init<S: StringProtocol>(_ title: S, text: Binding<String>,
|
|
14
|
+
onEditingChanged: @escaping (Bool) -> Void = { _ in }, onCommit: @escaping () -> Void) {
|
|
15
|
+
self.init(placeholder: String(title), text: text, secure: false)
|
|
16
|
+
self.onCommit = onCommit
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
extension Menu where Label == Text {
|
|
21
|
+
/// A menu titled with a localized key (the key's text: no string tables are loaded).
|
|
22
|
+
public init(_ titleKey: LocalizedStringKey, @ViewBuilder content: () -> Content) {
|
|
23
|
+
self.init(content: content) { Text(titleKey) }
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
extension Menu where Label == SwiftUI.Label<Text, Image> {
|
|
28
|
+
public init(_ titleKey: LocalizedStringKey, systemImage: String, @ViewBuilder content: () -> Content) {
|
|
29
|
+
self.init(content: content) { SwiftUI.Label(titleKey, systemImage: systemImage) }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
nonisolated extension View {
|
|
34
|
+
/// Search with a binding to whether the field is active. The page's
|
|
35
|
+
/// search field is always shown: `isPresented` is not changed by the
|
|
36
|
+
/// viewer (approximation).
|
|
37
|
+
public func searchable(text: Binding<String>, isPresented: Binding<Bool>, placement: SearchFieldPlacement = .automatic,
|
|
38
|
+
prompt: Text? = nil) -> some View {
|
|
39
|
+
searchable(text: text, placement: placement, prompt: prompt)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public func searchable(text: Binding<String>, isPresented: Binding<Bool>, placement: SearchFieldPlacement = .automatic,
|
|
43
|
+
prompt: LocalizedStringKey) -> some View {
|
|
44
|
+
searchable(text: text, placement: placement, prompt: prompt)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@_disfavoredOverload
|
|
48
|
+
public func searchable<S: StringProtocol>(text: Binding<String>, isPresented: Binding<Bool>,
|
|
49
|
+
placement: SearchFieldPlacement = .automatic, prompt: S) -> some View {
|
|
50
|
+
searchable(text: text, placement: placement, prompt: prompt)
|
|
51
|
+
}
|
|
52
|
+
}
|