@swiftbrowser/swift 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/Package.swift +23 -0
  2. package/README.md +12 -2
  3. package/Sources/Charts/Charts.swift +65 -0
  4. package/Sources/FoundationBridge/FoundationBridge.swift +77 -0
  5. package/Sources/FoundationBridge/Locale.swift +86 -0
  6. package/Sources/FoundationBridge/Timer.swift +48 -0
  7. package/Sources/FoundationBridge/UITextChecker.swift +30 -0
  8. package/Sources/FoundationLite/Common.swift +3 -0
  9. package/Sources/FoundationLite/FoundationLite.swift +171 -0
  10. package/Sources/FoundationLite/Interaction.swift +146 -0
  11. package/Sources/FoundationLite/Timer.swift +48 -0
  12. package/Sources/SwiftUI/API/Accessibility.swift +132 -0
  13. package/Sources/SwiftUI/API/Alert.swift +221 -0
  14. package/Sources/SwiftUI/API/Animation.swift +253 -0
  15. package/Sources/SwiftUI/API/App.swift +57 -0
  16. package/Sources/SwiftUI/API/Color.swift +161 -0
  17. package/Sources/SwiftUI/API/Controls.swift +268 -0
  18. package/Sources/SwiftUI/API/CustomLayout.swift +403 -0
  19. package/Sources/SwiftUI/API/Decorations.swift +92 -0
  20. package/Sources/SwiftUI/API/EditMode.swift +269 -0
  21. package/Sources/SwiftUI/API/Effects.swift +290 -0
  22. package/Sources/SwiftUI/API/Environment.swift +355 -0
  23. package/Sources/SwiftUI/API/Focus.swift +129 -0
  24. package/Sources/SwiftUI/API/Font.swift +131 -0
  25. package/Sources/SwiftUI/API/Gauge.swift +289 -0
  26. package/Sources/SwiftUI/API/Geometry.swift +89 -0
  27. package/Sources/SwiftUI/API/GeometryReader.swift +124 -0
  28. package/Sources/SwiftUI/API/Gestures.swift +342 -0
  29. package/Sources/SwiftUI/API/Grid.swift +188 -0
  30. package/Sources/SwiftUI/API/LabelStyle.swift +117 -0
  31. package/Sources/SwiftUI/API/Layout.swift +165 -0
  32. package/Sources/SwiftUI/API/Lists.swift +276 -0
  33. package/Sources/SwiftUI/API/Menu.swift +51 -0
  34. package/Sources/SwiftUI/API/Modifiers.swift +243 -0
  35. package/Sources/SwiftUI/API/Navigation.swift +371 -0
  36. package/Sources/SwiftUI/API/ObservableObject.swift +279 -0
  37. package/Sources/SwiftUI/API/OnChange.swift +48 -0
  38. package/Sources/SwiftUI/API/Phase8Controls.swift +394 -0
  39. package/Sources/SwiftUI/API/Picker.swift +176 -0
  40. package/Sources/SwiftUI/API/Presentation.swift +109 -0
  41. package/Sources/SwiftUI/API/Primitives.swift +390 -0
  42. package/Sources/SwiftUI/API/Publishers.swift +107 -0
  43. package/Sources/SwiftUI/API/RowActions.swift +83 -0
  44. package/Sources/SwiftUI/API/ScrollTargets.swift +104 -0
  45. package/Sources/SwiftUI/API/ShapeStyle.swift +311 -0
  46. package/Sources/SwiftUI/API/State.swift +127 -0
  47. package/Sources/SwiftUI/API/StrokeStyle.swift +30 -0
  48. package/Sources/SwiftUI/API/Tabs.swift +119 -0
  49. package/Sources/SwiftUI/API/Tasks.swift +35 -0
  50. package/Sources/SwiftUI/API/Timeline.swift +212 -0
  51. package/Sources/SwiftUI/API/Toolbar.swift +215 -0
  52. package/Sources/SwiftUI/API/View.swift +207 -0
  53. package/Sources/SwiftUI/API/ViewModifier.swift +40 -0
  54. package/Sources/SwiftUI/API/VisualEffect.swift +82 -0
  55. package/Sources/SwiftUI/Charts/Axis.swift +319 -0
  56. package/Sources/SwiftUI/Charts/Chart.swift +646 -0
  57. package/Sources/SwiftUI/Charts/ChartContent.swift +376 -0
  58. package/Sources/SwiftUI/Charts/Marks.swift +204 -0
  59. package/Sources/SwiftUI/Charts/Plottable.swift +99 -0
  60. package/Sources/SwiftUI/Charts/Scales.swift +326 -0
  61. package/Sources/SwiftUI/Runtime/Executor.swift +83 -0
  62. package/Sources/SwiftUI/Runtime/Exports.swift +65 -0
  63. package/Sources/SwiftUI/Runtime/JSON.swift +71 -0
  64. package/Sources/SwiftUI/Runtime/JSONParser.swift +443 -0
  65. package/Sources/SwiftUI/Runtime/Ops.swift +106 -0
  66. package/Sources/SwiftUI/Runtime/Reconciler.swift +210 -0
  67. package/Sources/SwiftUI/Runtime/Runtime.swift +1367 -0
  68. package/Sources/_FoundationCommon/Bundle.swift +82 -0
  69. package/Sources/_FoundationCommon/UserDefaults.swift +170 -0
  70. package/Sources/_FoundationCommon/_FoundationCommon.swift +10 -0
  71. package/index.d.ts +2 -0
  72. package/index.mjs +16 -0
  73. package/package.json +28 -4
  74. package/sync-sources.mjs +19 -0
@@ -0,0 +1,212 @@
1
+ // TimelineView: content re-rendered on a schedule. The shim has no Foundation
2
+ // `Date`, so the context carries `_TimelineDate` (seconds since 1970); Apple
3
+ // code reading `context.date.timeIntervalSince1970` compiles unchanged. Ticks
4
+ // run on the host-driven executor like `.task` does and stop when the view
5
+ // leaves the tree. See docs/ops-protocol.md (Phase 6).
6
+
7
+ #if canImport(WASILibc)
8
+ import WASILibc
9
+ #elseif canImport(Glibc)
10
+ import Glibc
11
+ #elseif canImport(Musl)
12
+ import Musl
13
+ #elseif canImport(Darwin)
14
+ import Darwin
15
+ #endif
16
+
17
+ // MARK: - Dates
18
+
19
+ /// A point in time, as seconds since 1970. Stands in for Foundation's `Date`.
20
+ public struct _TimelineDate: Hashable, Comparable, Sendable {
21
+ public var timeIntervalSince1970: Double
22
+
23
+ public init(timeIntervalSince1970: Double) {
24
+ self.timeIntervalSince1970 = timeIntervalSince1970
25
+ }
26
+
27
+ /// The current time (the wall clock; fixed in headless snapshots).
28
+ public static var now: _TimelineDate { _TimelineDate(timeIntervalSince1970: _timelineNow()) }
29
+
30
+ public func timeIntervalSince(_ other: _TimelineDate) -> Double {
31
+ timeIntervalSince1970 - other.timeIntervalSince1970
32
+ }
33
+
34
+ public func addingTimeInterval(_ interval: Double) -> _TimelineDate {
35
+ _TimelineDate(timeIntervalSince1970: timeIntervalSince1970 + interval)
36
+ }
37
+
38
+ public static func < (lhs: _TimelineDate, rhs: _TimelineDate) -> Bool {
39
+ lhs.timeIntervalSince1970 < rhs.timeIntervalSince1970
40
+ }
41
+ }
42
+
43
+ /// Seconds since 1970 from the platform clock.
44
+ func _wallClockSeconds() -> Double {
45
+ var tv = timeval()
46
+ gettimeofday(&tv, nil)
47
+ return Double(tv.tv_sec) + Double(tv.tv_usec) / 1_000_000
48
+ }
49
+
50
+ /// How `TimelineView` reads the time and waits between ticks. Headless
51
+ /// snapshot runs (`SB_HEADLESS`) derive the time from the executor's virtual
52
+ /// clock so output is byte-stable; tests swap both for a virtual clock.
53
+ /// Wasm is single-threaded and tests set these from the main actor.
54
+ enum _TimelineClock {
55
+ nonisolated(unsafe) static let headless = _environment("SB_HEADLESS") == "1"
56
+
57
+ nonisolated(unsafe) static var now: () -> Double = {
58
+ headless ? 1_700_000_000 + Double(_HostExecutor.shared.nowNs) / 1e9 : _wallClockSeconds()
59
+ }
60
+
61
+ nonisolated(unsafe) static var sleep: (Double) async -> Void = { seconds in
62
+ try? await Task.sleep(for: .seconds(seconds))
63
+ }
64
+ }
65
+
66
+ func _timelineNow() -> Double { _TimelineClock.now() }
67
+
68
+ // MARK: - Schedules
69
+
70
+ /// The mode a schedule produces entries for. Accepted for source compatibility.
71
+ public enum TimelineScheduleMode: Hashable, Sendable {
72
+ case normal, lowFrequency
73
+ }
74
+
75
+ /// A schedule of times at which a `TimelineView` updates. The shim's schedules
76
+ /// are periodic: an interval in seconds and a paused flag. A custom
77
+ /// conformance that does not adopt the shim's `_TimelineScheduling` ticks once
78
+ /// per second.
79
+ public protocol TimelineSchedule {}
80
+
81
+ /// What the runtime reads from a schedule.
82
+ protocol _TimelineScheduling {
83
+ /// Seconds between ticks.
84
+ var _interval: Double { get }
85
+ var _paused: Bool { get }
86
+ }
87
+
88
+ /// Updates as often as animation needs. The default minimum interval is 1/20 s:
89
+ /// every tick is a round trip through the browser renderer, so 60 fps costs
90
+ /// too much; pass `minimumInterval` to go faster or slower.
91
+ public struct AnimationTimelineSchedule: TimelineSchedule, _TimelineScheduling, Sendable {
92
+ public var minimumInterval: Double?
93
+ public var paused: Bool
94
+
95
+ public init(minimumInterval: Double? = nil, paused: Bool = false) {
96
+ self.minimumInterval = minimumInterval
97
+ self.paused = paused
98
+ }
99
+
100
+ var _interval: Double { max(minimumInterval ?? 0.05, 1.0 / 60) }
101
+ var _paused: Bool { paused }
102
+ }
103
+
104
+ /// Updates every `interval` seconds, starting at `startDate`.
105
+ public struct PeriodicTimelineSchedule: TimelineSchedule, _TimelineScheduling, Sendable {
106
+ public var startDate: _TimelineDate
107
+ public var interval: Double
108
+
109
+ public init(from startDate: _TimelineDate, by interval: Double) {
110
+ self.startDate = startDate
111
+ self.interval = interval
112
+ }
113
+
114
+ var _interval: Double { max(interval, 1.0 / 60) }
115
+ var _paused: Bool { false }
116
+ }
117
+
118
+ /// Updates at the start of every minute (every 60 seconds here).
119
+ public struct EveryMinuteTimelineSchedule: TimelineSchedule, _TimelineScheduling, Sendable {
120
+ public init() {}
121
+ var _interval: Double { 60 }
122
+ var _paused: Bool { false }
123
+ }
124
+
125
+ extension TimelineSchedule where Self == AnimationTimelineSchedule {
126
+ public static var animation: AnimationTimelineSchedule { AnimationTimelineSchedule() }
127
+ public static func animation(minimumInterval: Double? = nil, paused: Bool = false) -> AnimationTimelineSchedule {
128
+ AnimationTimelineSchedule(minimumInterval: minimumInterval, paused: paused)
129
+ }
130
+ }
131
+
132
+ extension TimelineSchedule where Self == PeriodicTimelineSchedule {
133
+ public static func periodic(from startDate: _TimelineDate, by interval: Double) -> PeriodicTimelineSchedule {
134
+ PeriodicTimelineSchedule(from: startDate, by: interval)
135
+ }
136
+ }
137
+
138
+ extension TimelineSchedule where Self == EveryMinuteTimelineSchedule {
139
+ public static var everyMinute: EveryMinuteTimelineSchedule { EveryMinuteTimelineSchedule() }
140
+ }
141
+
142
+ // MARK: - TimelineView
143
+
144
+ /// What a `TimelineView` passes to its content on every tick.
145
+ public struct TimelineViewDefaultContext {
146
+ /// The rate the view updates at.
147
+ public enum Cadence: Hashable, Sendable, Comparable {
148
+ case live, seconds, minutes
149
+ }
150
+
151
+ public var date: _TimelineDate
152
+ public var cadence: Cadence
153
+ }
154
+
155
+ /// Identity of a tick loop: when the schedule changes, the loop restarts.
156
+ struct _TimelineTaskID: Hashable {
157
+ var interval: Double
158
+ var paused: Bool
159
+ }
160
+
161
+ /// A view whose content updates on a schedule. Renders as a plain group with
162
+ /// a single child, `content(context)`.
163
+ public struct TimelineView<Schedule: TimelineSchedule, Content: View> {
164
+ public typealias Context = TimelineViewDefaultContext
165
+
166
+ let schedule: Schedule
167
+ let content: (Context) -> Content
168
+ /// The time of the last tick; `nil` before the first.
169
+ @State var tick: Double? = nil
170
+
171
+ public init(_ schedule: Schedule, @ViewBuilder content: @escaping (Context) -> Content) {
172
+ self.schedule = schedule
173
+ self.content = content
174
+ }
175
+
176
+ var interval: Double { (schedule as? any _TimelineScheduling)?._interval ?? 1 }
177
+ var paused: Bool { (schedule as? any _TimelineScheduling)?._paused ?? false }
178
+
179
+ var cadence: Context.Cadence {
180
+ interval >= 60 ? .minutes : interval >= 1 ? .seconds : .live
181
+ }
182
+ }
183
+ extension TimelineView: _PrimitiveView {}
184
+ extension TimelineView: _GroupView {
185
+ var _children: [any View] {
186
+ let date = _TimelineDate(timeIntervalSince1970: tick ?? _timelineNow())
187
+ return [content(Context(date: date, cadence: cadence))]
188
+ }
189
+ }
190
+ extension TimelineView: _TaskStarting {
191
+ var _taskID: AnyHashable? { AnyHashable(_TimelineTaskID(interval: interval, paused: paused)) }
192
+
193
+ var _taskAction: @Sendable () async -> Void {
194
+ guard !paused else { return {} }
195
+ let interval = self.interval
196
+ // The box is only touched on the main actor; the closure is built
197
+ // outside it, so the capture is marked as such.
198
+ let box = _TickBox(_tick.box)
199
+ return { @MainActor in
200
+ while !Task.isCancelled {
201
+ await _TimelineClock.sleep(interval)
202
+ if Task.isCancelled { return }
203
+ box.box.value = _timelineNow()
204
+ }
205
+ }
206
+ }
207
+ }
208
+
209
+ private struct _TickBox: @unchecked Sendable {
210
+ let box: _StateBox<Double?>
211
+ init(_ box: _StateBox<Double?>) { self.box = box }
212
+ }
@@ -0,0 +1,215 @@
1
+ /// `.toolbar` and `.searchable`. Items become `toolbar` / `searchfield`
2
+ /// elements inside the screen's content; when the runtime emits a `navscreen`
3
+ /// it hoists them to be the screen's direct children (after the content), which
4
+ /// is where the renderer draws the navigation bar from.
5
+
6
+ public struct ToolbarItemPlacement: Hashable, Sendable {
7
+ let name: String
8
+
9
+ public static let automatic = ToolbarItemPlacement(name: "trailing")
10
+ public static let primaryAction = ToolbarItemPlacement(name: "trailing")
11
+ public static let secondaryAction = ToolbarItemPlacement(name: "trailing")
12
+ public static let confirmationAction = ToolbarItemPlacement(name: "trailing")
13
+ public static let destructiveAction = ToolbarItemPlacement(name: "trailing")
14
+ public static let cancellationAction = ToolbarItemPlacement(name: "leading")
15
+ public static let navigation = ToolbarItemPlacement(name: "leading")
16
+ public static let principal = ToolbarItemPlacement(name: "principal")
17
+ public static let status = ToolbarItemPlacement(name: "bottom")
18
+ public static let bottomBar = ToolbarItemPlacement(name: "bottom")
19
+ public static let keyboard = ToolbarItemPlacement(name: "bottom")
20
+ public static let topBarLeading = ToolbarItemPlacement(name: "leading")
21
+ public static let topBarTrailing = ToolbarItemPlacement(name: "trailing")
22
+ public static let navigationBarLeading = ToolbarItemPlacement(name: "leading")
23
+ public static let navigationBarTrailing = ToolbarItemPlacement(name: "trailing")
24
+ }
25
+
26
+ /// Content of a `.toolbar`: items, groups, title menus or plain views.
27
+ public protocol ToolbarContent {
28
+ var _toolbarItems: [any View] { get }
29
+ }
30
+
31
+ /// A `toolbar` element: the items of one placement.
32
+ struct _ToolbarGroupView {
33
+ let placement: ToolbarItemPlacement
34
+ let items: [any View]
35
+ }
36
+ extension _ToolbarGroupView: _PrimitiveView {}
37
+ extension _ToolbarGroupView: _HostView {
38
+ var _kind: String { "toolbar" }
39
+ func _props(in environment: EnvironmentValues) -> _JSON { .object(["placement": .string(placement.name)]) }
40
+ var _content: [any View] { items }
41
+ }
42
+ extension _ToolbarGroupView: _EnvironmentModifying {
43
+ /// Labels in a bar show their icon only, as on iOS (the title is the
44
+ /// accessibility label); an explicit `.labelStyle` inside an item still wins.
45
+ func _apply(to environment: inout EnvironmentValues) {
46
+ environment._labelStyle = _AnyLabelStyle.iconOnly
47
+ }
48
+ }
49
+
50
+ public struct ToolbarItem<ID, Content: View>: ToolbarContent {
51
+ let placement: ToolbarItemPlacement
52
+ let content: Content
53
+
54
+ public init(placement: ToolbarItemPlacement = .automatic, @ViewBuilder content: () -> Content) where ID == Void {
55
+ self.placement = placement
56
+ self.content = content()
57
+ }
58
+
59
+ public init(id: ID, placement: ToolbarItemPlacement = .automatic, @ViewBuilder content: () -> Content) where ID: Hashable {
60
+ self.placement = placement
61
+ self.content = content()
62
+ }
63
+
64
+ public var _toolbarItems: [any View] { [_ToolbarGroupView(placement: placement, items: [content])] }
65
+ }
66
+
67
+ public struct ToolbarItemGroup<Content: View>: ToolbarContent {
68
+ let placement: ToolbarItemPlacement
69
+ let content: Content
70
+
71
+ public init(placement: ToolbarItemPlacement = .automatic, @ViewBuilder content: () -> Content) {
72
+ self.placement = placement
73
+ self.content = content()
74
+ }
75
+
76
+ public var _toolbarItems: [any View] { [_ToolbarGroupView(placement: placement, items: [content])] }
77
+ }
78
+
79
+ /// Accepted for source compatibility; the title has no menu in the browser.
80
+ public struct ToolbarTitleMenu<Content: View>: ToolbarContent {
81
+ public init(@ViewBuilder content: () -> Content) { _ = content() }
82
+ public var _toolbarItems: [any View] { [] }
83
+ }
84
+
85
+ struct _ToolbarContentList: ToolbarContent {
86
+ let parts: [any ToolbarContent]
87
+ var _toolbarItems: [any View] { parts.flatMap(\._toolbarItems) }
88
+ }
89
+
90
+ struct _EmptyToolbarContent: ToolbarContent {
91
+ var _toolbarItems: [any View] { [] }
92
+ }
93
+
94
+ @resultBuilder
95
+ public enum ToolbarContentBuilder {
96
+ public static func buildBlock() -> some ToolbarContent { _EmptyToolbarContent() }
97
+
98
+ public static func buildBlock<C: ToolbarContent>(_ content: C) -> C { content }
99
+
100
+ public static func buildBlock<each C: ToolbarContent>(_ content: repeat each C) -> some ToolbarContent {
101
+ var parts: [any ToolbarContent] = []
102
+ for part in repeat each content { parts.append(part) }
103
+ return _ToolbarContentList(parts: parts)
104
+ }
105
+
106
+ public static func buildExpression<C: ToolbarContent>(_ content: C) -> C { content }
107
+
108
+ public static func buildOptional<C: ToolbarContent>(_ content: C?) -> some ToolbarContent {
109
+ _ToolbarContentList(parts: content.map { [$0] } ?? [])
110
+ }
111
+
112
+ public static func buildEither<T: ToolbarContent>(first: T) -> some ToolbarContent {
113
+ _ToolbarContentList(parts: [first])
114
+ }
115
+
116
+ public static func buildEither<F: ToolbarContent>(second: F) -> some ToolbarContent {
117
+ _ToolbarContentList(parts: [second])
118
+ }
119
+
120
+ public static func buildIf<C: ToolbarContent>(_ content: C?) -> some ToolbarContent {
121
+ _ToolbarContentList(parts: content.map { [$0] } ?? [])
122
+ }
123
+ }
124
+
125
+ /// The content plus its toolbar groups, as sibling children. The groups are
126
+ /// hoisted to the enclosing `navscreen` when elements are emitted.
127
+ struct _ToolbarView<Content: View> {
128
+ let content: Content
129
+ let groups: [any View]
130
+ }
131
+ extension _ToolbarView: _PrimitiveView {}
132
+ extension _ToolbarView: _GroupView {
133
+ var _children: [any View] { [content] + groups }
134
+ }
135
+
136
+ extension View {
137
+ public func toolbar<C: ToolbarContent>(@ToolbarContentBuilder content: () -> C) -> some View {
138
+ _ToolbarView(content: self, groups: content()._toolbarItems)
139
+ }
140
+
141
+ /// Plain views in a toolbar go to the trailing placement.
142
+ public func toolbar<V: View>(@ViewBuilder content: () -> V) -> some View {
143
+ _ToolbarView(content: self, groups: [_ToolbarGroupView(placement: .automatic, items: [content()])])
144
+ }
145
+
146
+ public func toolbar(_ visibility: Visibility, for bars: ToolbarPlacement...) -> some View { self }
147
+ public func toolbarBackground<S: ShapeStyle>(_ style: S, for bars: ToolbarPlacement...) -> some View { self }
148
+ public func toolbarBackground(_ visibility: Visibility, for bars: ToolbarPlacement...) -> some View { self }
149
+ public func toolbarColorScheme(_ colorScheme: ColorScheme?, for bars: ToolbarPlacement...) -> some View { self }
150
+ }
151
+
152
+ public enum Visibility: Hashable, Sendable { case automatic, visible, hidden }
153
+
154
+ public struct ToolbarPlacement: Hashable, Sendable {
155
+ let name: String
156
+ public static let automatic = ToolbarPlacement(name: "automatic")
157
+ public static let navigationBar = ToolbarPlacement(name: "navigationBar")
158
+ public static let tabBar = ToolbarPlacement(name: "tabBar")
159
+ public static let bottomBar = ToolbarPlacement(name: "bottomBar")
160
+ }
161
+
162
+ // MARK: - Searchable
163
+
164
+ /// A `searchfield` element bound to the search text; hoisted into the bar.
165
+ struct _SearchFieldView {
166
+ let text: Binding<String>
167
+ let prompt: String
168
+ }
169
+ extension _SearchFieldView: _PrimitiveView {}
170
+ extension _SearchFieldView: _HostView {
171
+ var _kind: String { "searchfield" }
172
+ func _props(in environment: EnvironmentValues) -> _JSON {
173
+ .object(["text": .string(text.wrappedValue), "placeholder": .string(prompt)])
174
+ }
175
+ var _handler: ((_JSON) -> Void)? {
176
+ let text = self.text
177
+ return { payload in
178
+ if case .string(let value) = payload { text.wrappedValue = value }
179
+ }
180
+ }
181
+ }
182
+
183
+ struct _SearchableView<Content: View> {
184
+ let content: Content
185
+ let field: _SearchFieldView
186
+ }
187
+ extension _SearchableView: _PrimitiveView {}
188
+ extension _SearchableView: _GroupView {
189
+ var _children: [any View] { [content, field] }
190
+ }
191
+
192
+ public struct SearchFieldPlacement: Hashable, Sendable {
193
+ let name: String
194
+ public static let automatic = SearchFieldPlacement(name: "automatic")
195
+ public static let toolbar = SearchFieldPlacement(name: "toolbar")
196
+ public static let sidebar = SearchFieldPlacement(name: "sidebar")
197
+ public static let navigationBarDrawer = SearchFieldPlacement(name: "navigationBarDrawer")
198
+ }
199
+
200
+ extension View {
201
+ public func searchable(text: Binding<String>, placement: SearchFieldPlacement = .automatic,
202
+ prompt: Text? = nil) -> some View {
203
+ _SearchableView(content: self, field: _SearchFieldView(text: text, prompt: prompt?.content ?? "Search"))
204
+ }
205
+
206
+ public func searchable<S: StringProtocol>(text: Binding<String>, placement: SearchFieldPlacement = .automatic,
207
+ prompt: S) -> some View {
208
+ _SearchableView(content: self, field: _SearchFieldView(text: text, prompt: String(prompt)))
209
+ }
210
+
211
+ public func searchable(text: Binding<String>, placement: SearchFieldPlacement = .automatic,
212
+ prompt: LocalizedStringKey) -> some View {
213
+ _SearchableView(content: self, field: _SearchFieldView(text: text, prompt: prompt.key))
214
+ }
215
+ }
@@ -0,0 +1,207 @@
1
+ /// A type that represents part of your app's user interface.
2
+ ///
3
+ /// Mirrors SwiftUI's declaration, including main-actor isolation, so that a
4
+ /// type conforming to `View` in its primary declaration is main-actor
5
+ /// isolated on every platform.
6
+ @MainActor @preconcurrency
7
+ public protocol View {
8
+ associatedtype Body: View
9
+ @ViewBuilder var body: Self.Body { get }
10
+ }
11
+
12
+ extension Never: View {
13
+ public typealias Body = Never
14
+ public var body: Never { fatalError("Never has no body") }
15
+ }
16
+
17
+ /// Views the runtime renders directly instead of evaluating `body`.
18
+ public protocol _PrimitiveView: View where Body == Never {}
19
+
20
+ extension _PrimitiveView {
21
+ public var body: Never {
22
+ fatalError("\(Self.self) is a primitive view and has no body")
23
+ }
24
+ }
25
+
26
+ /// A view whose only job is to contribute other views to its parent.
27
+ /// Tuples, optionals, conditionals, `Group`, `AnyView` and `ForEach` are groups.
28
+ protocol _GroupView {
29
+ var _children: [any View] { get }
30
+ /// Stable identities for `_children`, when the group is keyed (`ForEach`).
31
+ var _keys: [AnyHashable]? { get }
32
+ }
33
+
34
+ extension _GroupView {
35
+ var _keys: [AnyHashable]? { nil }
36
+ }
37
+
38
+ /// A view that becomes an element in the render tree (see docs/ops-protocol.md).
39
+ protocol _HostView {
40
+ var _kind: String { get }
41
+ func _props(in environment: EnvironmentValues) -> _JSON
42
+ var _content: [any View] { get }
43
+ /// Receives the event payload (`.null` for a tap).
44
+ var _handler: ((_JSON) -> Void)? { get }
45
+ }
46
+
47
+ extension _HostView {
48
+ var _content: [any View] { [] }
49
+ var _handler: ((_JSON) -> Void)? { nil }
50
+ }
51
+
52
+ // MARK: - ViewBuilder
53
+
54
+ @resultBuilder
55
+ public enum ViewBuilder {
56
+ public static func buildBlock() -> EmptyView { EmptyView() }
57
+
58
+ public static func buildBlock<Content: View>(_ content: Content) -> Content { content }
59
+
60
+ public static func buildBlock<each Content: View>(
61
+ _ content: repeat each Content
62
+ ) -> TupleView<(repeat each Content)> {
63
+ var children: [any View] = []
64
+ for child in repeat each content { children.append(child) }
65
+ return TupleView((repeat each content), children: children)
66
+ }
67
+
68
+ public static func buildExpression<Content: View>(_ content: Content) -> Content { content }
69
+
70
+ public static func buildOptional<Content: View>(_ content: Content?) -> Content? { content }
71
+
72
+ public static func buildEither<TrueContent: View, FalseContent: View>(
73
+ first: TrueContent
74
+ ) -> _ConditionalContent<TrueContent, FalseContent> {
75
+ _ConditionalContent(storage: .trueContent(first))
76
+ }
77
+
78
+ public static func buildEither<TrueContent: View, FalseContent: View>(
79
+ second: FalseContent
80
+ ) -> _ConditionalContent<TrueContent, FalseContent> {
81
+ _ConditionalContent(storage: .falseContent(second))
82
+ }
83
+
84
+ public static func buildLimitedAvailability<Content: View>(_ content: Content) -> AnyView {
85
+ AnyView(content)
86
+ }
87
+ }
88
+
89
+ // MARK: - Structural views
90
+
91
+ public struct EmptyView: Sendable {
92
+ public init() {}
93
+ }
94
+ extension EmptyView: _PrimitiveView {}
95
+ extension EmptyView: _GroupView {
96
+ var _children: [any View] { [] }
97
+ }
98
+
99
+ public struct TupleView<T> {
100
+ public var value: T
101
+ let children: [any View]
102
+
103
+ init(_ value: T, children: [any View]) {
104
+ self.value = value
105
+ self.children = children
106
+ }
107
+ }
108
+ extension TupleView: _PrimitiveView {}
109
+ extension TupleView: _GroupView {
110
+ var _children: [any View] { children }
111
+ }
112
+
113
+ public struct _ConditionalContent<TrueContent: View, FalseContent: View> {
114
+ enum Storage {
115
+ case trueContent(TrueContent)
116
+ case falseContent(FalseContent)
117
+ }
118
+ let storage: Storage
119
+ }
120
+ extension _ConditionalContent: _PrimitiveView {}
121
+ extension _ConditionalContent: _GroupView {
122
+ var _children: [any View] {
123
+ switch storage {
124
+ case .trueContent(let view): [view]
125
+ case .falseContent(let view): [view]
126
+ }
127
+ }
128
+ }
129
+
130
+ extension Optional: View where Wrapped: View {
131
+ public typealias Body = Never
132
+ public var body: Never { fatalError("Optional is a primitive view and has no body") }
133
+ }
134
+ extension Optional: _GroupView where Wrapped: View {
135
+ var _children: [any View] {
136
+ switch self {
137
+ case .some(let view): [view]
138
+ case .none: []
139
+ }
140
+ }
141
+ }
142
+
143
+ /// A type-erased view.
144
+ public struct AnyView {
145
+ let storage: any View
146
+
147
+ public init<V: View>(_ view: V) { storage = view }
148
+ public init<V: View>(erasing view: V) { storage = view }
149
+ }
150
+ extension AnyView: _PrimitiveView {}
151
+ extension AnyView: _GroupView {
152
+ var _children: [any View] { [storage] }
153
+ }
154
+
155
+ /// Collects views without affecting layout.
156
+ public struct Group<Content: View> {
157
+ let content: Content
158
+ public init(@ViewBuilder content: () -> Content) { self.content = content() }
159
+ }
160
+ extension Group: _PrimitiveView {}
161
+ extension Group: _GroupView {
162
+ var _children: [any View] { [content] }
163
+ }
164
+
165
+ /// A view that computes views on demand from a collection, keyed by identity.
166
+ /// `Content` is unconstrained so that the same type can produce chart content
167
+ /// (`ForEach` inside a `Chart`, see Charts/ChartContent.swift); the `View`
168
+ /// conformance applies when `Content` is a view.
169
+ public struct ForEach<Data: RandomAccessCollection, ID: Hashable, Content> {
170
+ public let data: Data
171
+ let id: (Data.Element) -> ID
172
+ let content: (Data.Element) -> Content
173
+
174
+ init(_data data: Data, id: @escaping (Data.Element) -> ID, content: @escaping (Data.Element) -> Content) {
175
+ self.data = data
176
+ self.id = id
177
+ self.content = content
178
+ }
179
+ }
180
+
181
+ extension ForEach where Content: View {
182
+ public init(_ data: Data, id: KeyPath<Data.Element, ID>,
183
+ @ViewBuilder content: @escaping (Data.Element) -> Content) {
184
+ self.init(_data: data, id: { $0[keyPath: id] }, content: content)
185
+ }
186
+ }
187
+
188
+ extension ForEach where Content: View, Data.Element: Identifiable, ID == Data.Element.ID {
189
+ public init(_ data: Data, @ViewBuilder content: @escaping (Data.Element) -> Content) {
190
+ self.init(_data: data, id: { $0.id }, content: content)
191
+ }
192
+ }
193
+
194
+ extension ForEach where Content: View, Data == Range<Int>, ID == Int {
195
+ public init(_ data: Range<Int>, @ViewBuilder content: @escaping (Int) -> Content) {
196
+ self.init(_data: data, id: { $0 }, content: content)
197
+ }
198
+ }
199
+
200
+ extension ForEach: View where Content: View {
201
+ public typealias Body = Never
202
+ }
203
+ extension ForEach: _PrimitiveView where Content: View {}
204
+ extension ForEach: _GroupView where Content: View {
205
+ var _children: [any View] { data.map { content($0) } }
206
+ var _keys: [AnyHashable]? { data.map { AnyHashable(id($0)) } }
207
+ }
@@ -0,0 +1,40 @@
1
+ /// A reusable modifier: `body(content:)` wraps the view it is applied to.
2
+ /// `@State` and `@Environment` properties declared on a modifier are managed
3
+ /// by the runtime like a view's (it reflects into the modifier of a
4
+ /// `ModifiedContent`).
5
+ @MainActor @preconcurrency
6
+ public protocol ViewModifier {
7
+ associatedtype Body: View
8
+ typealias Content = _ViewModifier_Content<Self>
9
+ @ViewBuilder func body(content: Content) -> Body
10
+ }
11
+
12
+ /// The view a modifier's `body(content:)` receives: the modified view, erased.
13
+ public struct _ViewModifier_Content<Modifier: ViewModifier> {
14
+ let view: AnyView
15
+ }
16
+ extension _ViewModifier_Content: _PrimitiveView {}
17
+ extension _ViewModifier_Content: _GroupView {
18
+ var _children: [any View] { [view] }
19
+ }
20
+
21
+ /// A view with a `ViewModifier` applied.
22
+ public struct ModifiedContent<Content: View, Modifier: ViewModifier>: View {
23
+ public var content: Content
24
+ public var modifier: Modifier
25
+
26
+ public init(content: Content, modifier: Modifier) {
27
+ self.content = content
28
+ self.modifier = modifier
29
+ }
30
+
31
+ public var body: some View {
32
+ modifier.body(content: _ViewModifier_Content(view: AnyView(content)))
33
+ }
34
+ }
35
+
36
+ extension View {
37
+ public func modifier<M: ViewModifier>(_ modifier: M) -> ModifiedContent<Self, M> {
38
+ ModifiedContent(content: self, modifier: modifier)
39
+ }
40
+ }