@swiftbrowser/native 0.18.3 → 0.21.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/README.md +53 -11
- package/bin/swiftbrowser-native.mjs +47 -0
- package/dist/blob.d.ts +85 -11
- package/dist/blob.d.ts.map +1 -1
- package/dist/blob.js +184 -34
- package/dist/blob.js.map +1 -1
- package/dist/bridge.d.ts +66 -8
- package/dist/bridge.d.ts.map +1 -1
- package/dist/bridge.js.map +1 -1
- package/dist/encoding.d.ts +13 -0
- package/dist/encoding.d.ts.map +1 -1
- package/dist/encoding.js +24 -0
- package/dist/encoding.js.map +1 -1
- package/dist/fetch.d.ts +27 -13
- package/dist/fetch.d.ts.map +1 -1
- package/dist/fetch.js +117 -45
- package/dist/fetch.js.map +1 -1
- package/dist/hostRequests.d.ts +24 -6
- package/dist/hostRequests.d.ts.map +1 -1
- package/dist/hostRequests.js +108 -15
- package/dist/hostRequests.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/ios.d.ts +79 -0
- package/dist/ios.d.ts.map +1 -0
- package/dist/ios.js +181 -0
- package/dist/ios.js.map +1 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +16 -7
- package/dist/runtime.js.map +1 -1
- package/dist/subtle.d.ts +1 -1
- package/dist/subtle.d.ts.map +1 -1
- package/dist/subtle.js +9 -5
- package/dist/subtle.js.map +1 -1
- package/dist/webSocket.d.ts +12 -5
- package/dist/webSocket.d.ts.map +1 -1
- package/dist/webSocket.js +37 -11
- package/dist/webSocket.js.map +1 -1
- package/package.json +7 -3
- package/src/blob.ts +215 -34
- package/src/bridge.ts +56 -8
- package/src/encoding.ts +22 -0
- package/src/fetch.ts +111 -48
- package/src/hostRequests.ts +129 -22
- package/src/index.ts +1 -2
- package/src/ios.ts +253 -0
- package/src/runtime.ts +23 -8
- package/src/subtle.ts +10 -6
- package/src/webSocket.ts +37 -13
- package/swift/Packages/SwiftBrowserDSP/Package.swift +31 -0
- package/swift/Packages/SwiftBrowserDSP/README.md +42 -0
- package/swift/Packages/SwiftBrowserDSP/Sources/SwiftBrowserDSP/ProgramNode.swift +306 -0
- package/swift/Packages/SwiftBrowserDSP/Sources/SwiftBrowserDSP/ScopeView.swift +79 -0
- package/swift/Packages/SwiftBrowserDSP/Sources/SwiftBrowserDSPCore/Machine.swift +1561 -0
- package/swift/Packages/SwiftBrowserDSP/Sources/SwiftBrowserDSPCore/Program.swift +483 -0
- package/swift/Packages/SwiftBrowserLocalModel/Package.swift +35 -0
- package/swift/Packages/SwiftBrowserLocalModel/README.md +66 -0
- package/swift/Packages/SwiftBrowserLocalModel/Sources/SwiftBrowserLocalModel/LlamaEngine.swift +223 -0
- package/swift/Packages/SwiftBrowserLocalModel/Sources/SwiftBrowserLocalModel/LocalLanguageModel.swift +409 -0
- package/swift/Packages/SwiftBrowserShader/Package.swift +31 -0
- package/swift/Packages/SwiftBrowserShader/README.md +45 -0
- package/swift/Packages/SwiftBrowserShader/Sources/SwiftBrowserShader/ShaderRenderer.swift +103 -0
- package/swift/Packages/SwiftBrowserShader/Sources/SwiftBrowserShader/ShaderView.swift +168 -0
- package/swift/Packages/SwiftBrowserShader/Sources/SwiftBrowserShaderCore/ShaderProps.swift +120 -0
- package/swift/Sources/SwiftBrowserNative/Host/AudioEngineBridge.swift +921 -0
- package/swift/Sources/SwiftBrowserNative/Host/AudioEngineNodes.swift +70 -0
- package/swift/Sources/SwiftBrowserNative/Host/BodyStream.swift +166 -0
- package/swift/Sources/SwiftBrowserNative/Host/FilesBridge.swift +144 -10
- package/swift/Sources/SwiftBrowserNative/Host/HostBridge.swift +151 -27
- package/swift/Sources/SwiftBrowserNative/Host/HostRequestBridge.swift +139 -40
- package/swift/Sources/SwiftBrowserNative/Host/JSHost.swift +116 -0
- package/swift/Sources/SwiftBrowserNative/Host/JavaScriptCoreHost.swift +141 -2
- package/swift/Sources/SwiftBrowserNative/Host/LanguageModelBridge.swift +511 -0
- package/swift/Sources/SwiftBrowserNative/Host/LanguageModelProvider.swift +195 -0
- package/swift/Sources/SwiftBrowserNative/Host/WebSocketBridge.swift +24 -11
- package/swift/Sources/SwiftBrowserNative/Host/WorkerHost.swift +3 -2
- package/swift/Sources/SwiftBrowserNative/Runtime/NativeFunctions.swift +1 -1
- package/swift/Sources/SwiftBrowserNative/Runtime/ViewRegistry.swift +57 -0
- package/swift/Sources/SwiftBrowserNative/Views/ElementView.swift +12 -7
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/Controls.swift +18 -1
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/Drawing.swift +378 -0
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/Lists.swift +41 -1
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/More.swift +88 -17
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/Navigation.swift +87 -21
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/PinchTracker.swift +200 -0
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/PopTap.swift +103 -0
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/Stacks.swift +3 -2
- package/swift/Sources/SwiftBrowserNative/Views/StyleModifier.swift +146 -0
- package/swift/Sources/SwiftBrowserProtocol/Drawing.swift +306 -0
- package/swift/Sources/SwiftBrowserProtocol/Props.swift +31 -2
- package/swift/Sources/SwiftBrowserProtocol/RendererEvent.swift +24 -3
- package/swift/Sources/SwiftBrowserProtocol/Style.swift +87 -0
- package/swift/Sources/SwiftBrowserTree/OpTree.swift +25 -0
- package/wasm/SwiftBrowserNative.wasm +2 -2
- package/dist/native.d.ts +0 -19
- package/dist/native.d.ts.map +0 -1
- package/dist/native.js +0 -17
- package/dist/native.js.map +0 -1
- package/src/native.ts +0 -35
- package/swift/Sources/SwiftBrowserNative/Runtime/NativeRegistry.swift +0 -53
|
@@ -38,6 +38,7 @@ public struct Style: Equatable, Sendable {
|
|
|
38
38
|
public var scale: PointSpec?
|
|
39
39
|
public var scaleAnchor: UnitPointSpec?
|
|
40
40
|
public var shadow: ShadowSpec?
|
|
41
|
+
public var compositingGroup: Bool
|
|
41
42
|
public var clipped: Bool
|
|
42
43
|
public var tint: ColorSpec?
|
|
43
44
|
public var position: PointSpec?
|
|
@@ -77,6 +78,22 @@ public struct Style: Equatable, Sendable {
|
|
|
77
78
|
public var scrollTo: ScrollToSpec?
|
|
78
79
|
/// `.scrollTransition`: the effects in its three phases.
|
|
79
80
|
public var scrollTransition: ScrollTransitionSpec?
|
|
81
|
+
/// `.gridCellColumns(_:)`: the `grid` columns the cell spans.
|
|
82
|
+
public var gridCellColumns: Int?
|
|
83
|
+
/// `.gridCellAnchor(_:)`: the unit point the cell aligns by in its cell.
|
|
84
|
+
public var gridCellAnchor: UnitPointSpec?
|
|
85
|
+
/// `.onScrollVisibilityChange(threshold:)`: the share of the box that counts as visible; the box sends `scrollvisibility`.
|
|
86
|
+
public var scrollVisibilityThreshold: Double?
|
|
87
|
+
/// `.alignmentGuide(_:computeValue:)`, as `a + b·width + c·height`.
|
|
88
|
+
public var alignmentGuide: AlignmentGuideSpec?
|
|
89
|
+
/// `.rotation3DEffect(_:axis:anchor:perspective:)`.
|
|
90
|
+
public var rotation3D: Rotation3DSpec?
|
|
91
|
+
/// `.onHover`: the box sends `hover` as a pointer enters and leaves it.
|
|
92
|
+
public var hover: Bool
|
|
93
|
+
/// `.pointerStyle(_:)`: the pointer over the box, by its CSS name (`pointer`, `text`, `grab`…).
|
|
94
|
+
public var pointerStyle: String?
|
|
95
|
+
/// `.symbolEffect(_:options:isActive:)` / `.symbolEffect(_:options:value:)`.
|
|
96
|
+
public var symbolEffect: SymbolEffectSpec?
|
|
80
97
|
|
|
81
98
|
public init(json: JSONValue) {
|
|
82
99
|
let fields = json.object ?? [:]
|
|
@@ -115,6 +132,7 @@ public struct Style: Equatable, Sendable {
|
|
|
115
132
|
scale = PointSpec(json: fields.value("scale"))
|
|
116
133
|
scaleAnchor = UnitPointSpec(json: fields.value("scaleAnchor"))
|
|
117
134
|
shadow = ShadowSpec(json: fields.value("shadow"))
|
|
135
|
+
compositingGroup = fields.bool("compositingGroup") ?? false
|
|
118
136
|
clipped = fields.bool("clipped") ?? false
|
|
119
137
|
tint = ColorSpec(json: fields.value("tint"))
|
|
120
138
|
position = PointSpec(json: fields.value("position"))
|
|
@@ -152,6 +170,14 @@ public struct Style: Equatable, Sendable {
|
|
|
152
170
|
scrollID = fields.bool("scrollID") ?? false
|
|
153
171
|
scrollTo = ScrollToSpec(json: fields.value("scrollTo"))
|
|
154
172
|
scrollTransition = ScrollTransitionSpec(json: fields.value("scrollTransition"))
|
|
173
|
+
gridCellColumns = fields.int("gridCellColumns")
|
|
174
|
+
gridCellAnchor = UnitPointSpec(json: fields.value("gridCellAnchor"))
|
|
175
|
+
scrollVisibilityThreshold = fields.object("scrollVisibility")?.double("threshold")
|
|
176
|
+
alignmentGuide = AlignmentGuideSpec(json: fields.value("alignmentGuide"))
|
|
177
|
+
rotation3D = Rotation3DSpec(json: fields.value("rotation3D"))
|
|
178
|
+
hover = fields.bool("hover") ?? false
|
|
179
|
+
pointerStyle = fields.string("pointerStyle")
|
|
180
|
+
symbolEffect = SymbolEffectSpec(json: fields.value("symbolEffect"))
|
|
155
181
|
}
|
|
156
182
|
|
|
157
183
|
public init() {
|
|
@@ -162,6 +188,67 @@ public struct Style: Equatable, Sendable {
|
|
|
162
188
|
public var fieldNames: [String] { raw.keys.sorted() }
|
|
163
189
|
}
|
|
164
190
|
|
|
191
|
+
/// `.alignmentGuide(_:computeValue:)`: the box's guide `name` on `axis` is `a + b·width + c·height`.
|
|
192
|
+
public struct AlignmentGuideSpec: Equatable, Sendable {
|
|
193
|
+
/// `horizontal` (a `HorizontalAlignment`: leading, center, trailing…) or `vertical`.
|
|
194
|
+
public var axis: String
|
|
195
|
+
public var name: String
|
|
196
|
+
public var a: Double
|
|
197
|
+
public var b: Double
|
|
198
|
+
public var c: Double
|
|
199
|
+
public init?(json: JSONValue) {
|
|
200
|
+
guard let fields = json.object, let axis = fields.string("axis"), let name = fields.string("name") else { return nil }
|
|
201
|
+
let value = (fields.array("value") ?? []).compactMap(\.double)
|
|
202
|
+
self.axis = axis
|
|
203
|
+
self.name = name
|
|
204
|
+
a = value.count > 0 ? value[0] : 0
|
|
205
|
+
b = value.count > 1 ? value[1] : 0
|
|
206
|
+
c = value.count > 2 ? value[2] : 0
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/// `.rotation3DEffect`: degrees about `axis`, around `anchor`, with SwiftUI's perspective factor.
|
|
211
|
+
public struct Rotation3DSpec: Equatable, Sendable {
|
|
212
|
+
public var angle: Double
|
|
213
|
+
public var x: Double
|
|
214
|
+
public var y: Double
|
|
215
|
+
public var z: Double
|
|
216
|
+
public var anchor: UnitPointSpec?
|
|
217
|
+
public var perspective: Double
|
|
218
|
+
public init?(json: JSONValue) {
|
|
219
|
+
guard let fields = json.object, let angle = fields.double("angle") else { return nil }
|
|
220
|
+
self.angle = angle
|
|
221
|
+
let axis = fields.object("axis") ?? [:]
|
|
222
|
+
x = axis.double("x") ?? 0
|
|
223
|
+
y = axis.double("y") ?? 0
|
|
224
|
+
z = axis.double("z") ?? 0
|
|
225
|
+
anchor = UnitPointSpec(json: fields.value("anchor"))
|
|
226
|
+
perspective = fields.double("perspective") ?? 1
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/// An SF Symbols effect: `effect` by name (`bounce`, `pulse`, `variableColor`, `scale`, `rotate`, `wiggle`,
|
|
231
|
+
/// `breathe`, `appear`, `disappear`), run while the style has it, or once each time `trigger` changes.
|
|
232
|
+
public struct SymbolEffectSpec: Equatable, Sendable {
|
|
233
|
+
public var effect: String
|
|
234
|
+
/// `up` or `down`.
|
|
235
|
+
public var direction: String?
|
|
236
|
+
public var speed: Double?
|
|
237
|
+
/// How many times a run repeats; nil with `forever` false for the effect's own.
|
|
238
|
+
public var repeatCount: Int?
|
|
239
|
+
public var forever: Bool
|
|
240
|
+
public var trigger: Double?
|
|
241
|
+
public init?(json: JSONValue) {
|
|
242
|
+
guard let fields = json.object, let effect = fields.string("effect") else { return nil }
|
|
243
|
+
self.effect = effect
|
|
244
|
+
direction = fields.string("direction")
|
|
245
|
+
speed = fields.double("speed")
|
|
246
|
+
repeatCount = fields.int("repeat")
|
|
247
|
+
forever = fields.string("repeat") == "forever"
|
|
248
|
+
trigger = fields.double("trigger")
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
165
252
|
/// `.containerRelativeFrame(axes)` as the style hint carries it: `length = a × container + b`.
|
|
166
253
|
public struct ContainerRelativeFrameSpec: Equatable, Sendable {
|
|
167
254
|
public var horizontal: Bool
|
|
@@ -26,6 +26,13 @@ public final class OpTree {
|
|
|
26
26
|
/// The root container, element id 0.
|
|
27
27
|
public let root: ElementNode
|
|
28
28
|
private var nodes: [Int: ElementNode]
|
|
29
|
+
/// Screens (`navscreen`) removed lately, with when: a navigation stack
|
|
30
|
+
/// keeps drawing one through the pop that takes it off (SwiftUI asks
|
|
31
|
+
/// the destination for it while the transition runs; the app has
|
|
32
|
+
/// removed it by then, synchronously on the pop's tap).
|
|
33
|
+
private var poppedScreens: [Int: (node: ElementNode, at: Date)] = [:]
|
|
34
|
+
/// How long a popped screen stays available: past any pop transition.
|
|
35
|
+
private static let poppedScreenLife: TimeInterval = 3
|
|
29
36
|
/// The ops of the current pass, until its `commit`.
|
|
30
37
|
private var pending: [Op] = []
|
|
31
38
|
/// How many passes were committed.
|
|
@@ -57,6 +64,18 @@ public final class OpTree {
|
|
|
57
64
|
/// The element with this id, when it exists (created and not removed).
|
|
58
65
|
public func node(_ id: Int) -> ElementNode? { nodes[id] }
|
|
59
66
|
|
|
67
|
+
/// A `navscreen` removed within the last moments (`poppedScreenLife`),
|
|
68
|
+
/// its subtree as it was: what a stack shows while the pop that removed
|
|
69
|
+
/// it is still animating. Nil once it is forgotten or its id reused.
|
|
70
|
+
public func poppedScreen(_ id: Int) -> ElementNode? {
|
|
71
|
+
guard let entry = poppedScreens[id] else { return nil }
|
|
72
|
+
guard Date().timeIntervalSince(entry.at) < Self.poppedScreenLife else {
|
|
73
|
+
poppedScreens[id] = nil
|
|
74
|
+
return nil
|
|
75
|
+
}
|
|
76
|
+
return entry.node
|
|
77
|
+
}
|
|
78
|
+
|
|
60
79
|
/// How many elements exist, the root included.
|
|
61
80
|
public var count: Int { nodes.count }
|
|
62
81
|
|
|
@@ -126,6 +145,7 @@ public final class OpTree {
|
|
|
126
145
|
// for a live id replaces it in place (the web renderer does the same).
|
|
127
146
|
detach(existing)
|
|
128
147
|
}
|
|
148
|
+
poppedScreens[id] = nil
|
|
129
149
|
nodes[id] = ElementNode(id: id, kind: kind, props: props)
|
|
130
150
|
case .update(let id, let props):
|
|
131
151
|
guard let node = nodes[id] else { throw OpTreeError("update: no element \(id)") }
|
|
@@ -155,6 +175,11 @@ public final class OpTree {
|
|
|
155
175
|
guard let node = nodes[id] else { throw OpTreeError("remove: no element \(id)") }
|
|
156
176
|
detach(node)
|
|
157
177
|
forget(node)
|
|
178
|
+
if node.kind == "navscreen" {
|
|
179
|
+
let now = Date()
|
|
180
|
+
poppedScreens = poppedScreens.filter { now.timeIntervalSince($0.value.at) < Self.poppedScreenLife }
|
|
181
|
+
poppedScreens[id] = (node, now)
|
|
182
|
+
}
|
|
158
183
|
case .commit:
|
|
159
184
|
break
|
|
160
185
|
case .other(let name, let fields):
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: package/wasm/SwiftBrowserNative.wasm
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 50264748 bytes
|
|
4
|
+
sha256: 7712613a6aed40822224c0e4042dab20c7de0e5708ff6ad8ce79f52013b76472
|
package/dist/native.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `<Native name props onEvent>`: the escape hatch (docs/native.md). The
|
|
3
|
-
* element kind `native`, which only this producer emits, names a SwiftUI view
|
|
4
|
-
* the app registered with `NativeRegistry.register(name)`; `props` reach it
|
|
5
|
-
* as JSON, its children as SwiftUI views, and what it sends back arrives at
|
|
6
|
-
* `onEvent`.
|
|
7
|
-
*/
|
|
8
|
-
import { type ReactElement, type ReactNode } from 'react';
|
|
9
|
-
export interface NativeProps {
|
|
10
|
-
/** The name registered on the Swift side. */
|
|
11
|
-
name: string;
|
|
12
|
-
/** JSON-serializable props for the view. */
|
|
13
|
-
props?: Record<string, unknown>;
|
|
14
|
-
/** A value the native view sent (`context.send(value)`). */
|
|
15
|
-
onEvent?: (value: unknown) => void;
|
|
16
|
-
children?: ReactNode;
|
|
17
|
-
}
|
|
18
|
-
export declare function Native({ name, props, onEvent, children }: NativeProps): ReactElement;
|
|
19
|
-
//# sourceMappingURL=native.d.ts.map
|
package/dist/native.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAiB,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEzE,MAAM,WAAW,WAAW;IAC1B,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,4DAA4D;IAC5D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAUD,wBAAgB,MAAM,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,WAAW,GAAG,YAAY,CAMpF"}
|
package/dist/native.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `<Native name props onEvent>`: the escape hatch (docs/native.md). The
|
|
3
|
-
* element kind `native`, which only this producer emits, names a SwiftUI view
|
|
4
|
-
* the app registered with `NativeRegistry.register(name)`; `props` reach it
|
|
5
|
-
* as JSON, its children as SwiftUI views, and what it sends back arrives at
|
|
6
|
-
* `onEvent`.
|
|
7
|
-
*/
|
|
8
|
-
import { createElement } from 'react';
|
|
9
|
-
export function Native({ name, props, onEvent, children }) {
|
|
10
|
-
// The host element of @swiftbrowser/react: `sb` with the protocol kind and
|
|
11
|
-
// props, and the handlers by event type (packages/react/src/host/instance.ts).
|
|
12
|
-
const hostProps = { kind: 'native', props: { name, props: props ?? {} } };
|
|
13
|
-
if (onEvent)
|
|
14
|
-
hostProps['on'] = { native: (event) => onEvent(event.value) };
|
|
15
|
-
return children === undefined ? createElement('sb', hostProps) : createElement('sb', hostProps, children);
|
|
16
|
-
}
|
|
17
|
-
//# sourceMappingURL=native.js.map
|
package/dist/native.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"native.js","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,aAAa,EAAqC,MAAM,OAAO,CAAC;AAoBzE,MAAM,UAAU,MAAM,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAe;IACpE,2EAA2E;IAC3E,+EAA+E;IAC/E,MAAM,SAAS,GAA4B,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE,EAAE,EAAE,CAAC;IACnG,IAAI,OAAO;QAAE,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,KAAkB,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;IACxF,OAAO,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;AAC5G,CAAC"}
|
package/src/native.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `<Native name props onEvent>`: the escape hatch (docs/native.md). The
|
|
3
|
-
* element kind `native`, which only this producer emits, names a SwiftUI view
|
|
4
|
-
* the app registered with `NativeRegistry.register(name)`; `props` reach it
|
|
5
|
-
* as JSON, its children as SwiftUI views, and what it sends back arrives at
|
|
6
|
-
* `onEvent`.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { createElement, type ReactElement, type ReactNode } from 'react';
|
|
10
|
-
|
|
11
|
-
export interface NativeProps {
|
|
12
|
-
/** The name registered on the Swift side. */
|
|
13
|
-
name: string;
|
|
14
|
-
/** JSON-serializable props for the view. */
|
|
15
|
-
props?: Record<string, unknown>;
|
|
16
|
-
/** A value the native view sent (`context.send(value)`). */
|
|
17
|
-
onEvent?: (value: unknown) => void;
|
|
18
|
-
children?: ReactNode;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** The shape of a `native` event (`RendererEvent` has no such member). */
|
|
22
|
-
interface NativeEvent {
|
|
23
|
-
type: 'native';
|
|
24
|
-
id: number;
|
|
25
|
-
name: string;
|
|
26
|
-
value: unknown;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function Native({ name, props, onEvent, children }: NativeProps): ReactElement {
|
|
30
|
-
// The host element of @swiftbrowser/react: `sb` with the protocol kind and
|
|
31
|
-
// props, and the handlers by event type (packages/react/src/host/instance.ts).
|
|
32
|
-
const hostProps: Record<string, unknown> = { kind: 'native', props: { name, props: props ?? {} } };
|
|
33
|
-
if (onEvent) hostProps['on'] = { native: (event: NativeEvent) => onEvent(event.value) };
|
|
34
|
-
return children === undefined ? createElement('sb', hostProps) : createElement('sb', hostProps, children);
|
|
35
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
#if canImport(SwiftUI)
|
|
2
|
-
import Foundation
|
|
3
|
-
import SwiftBrowserProtocol
|
|
4
|
-
import SwiftBrowserTree
|
|
5
|
-
import SwiftUI
|
|
6
|
-
|
|
7
|
-
/// What a registered native view gets: the element's `props` as the app
|
|
8
|
-
/// wrote them, its children (SwiftUI views the app put inside it) and a way
|
|
9
|
-
/// to send a value back, which reaches the element's `onEvent` in JavaScript.
|
|
10
|
-
public struct NativeViewContext {
|
|
11
|
-
public let id: Int
|
|
12
|
-
public let props: JSONValue
|
|
13
|
-
public let children: [ElementNode]
|
|
14
|
-
public let send: (JSONValue) -> Void
|
|
15
|
-
|
|
16
|
-
/// The children as a view (built on the main actor, as views are).
|
|
17
|
-
@MainActor public var content: some View {
|
|
18
|
-
ForEach(children) { ElementView(node: $0) }
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/// The escape hatch (docs/native.md): the `native` element kind, which only
|
|
23
|
-
/// a JavaScript producer emits, names a view the Swift app registered.
|
|
24
|
-
///
|
|
25
|
-
/// ```swift
|
|
26
|
-
/// NativeRegistry.register("MapView") { context in
|
|
27
|
-
/// MapView(region: context.props["region"]) { context.send(["tapped": true]) }
|
|
28
|
-
/// }
|
|
29
|
-
/// ```
|
|
30
|
-
///
|
|
31
|
-
/// ```tsx
|
|
32
|
-
/// <Native name="MapView" props={{ region }} onEvent={(value) => …} />
|
|
33
|
-
/// ```
|
|
34
|
-
@MainActor
|
|
35
|
-
public enum NativeRegistry {
|
|
36
|
-
public typealias Builder = (NativeViewContext) -> AnyView
|
|
37
|
-
private static var builders: [String: Builder] = [:]
|
|
38
|
-
|
|
39
|
-
public static func register<V: View>(_ name: String, @ViewBuilder _ builder: @escaping (NativeViewContext) -> V) {
|
|
40
|
-
builders[name] = { AnyView(builder($0)) }
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
public static func unregister(_ name: String) {
|
|
44
|
-
builders[name] = nil
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
public static func isRegistered(_ name: String) -> Bool { builders[name] != nil }
|
|
48
|
-
|
|
49
|
-
static func view(name: String, context: NativeViewContext) -> AnyView? {
|
|
50
|
-
builders[name]?(context)
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
#endif
|