@swiftbrowser/native 0.14.1 → 0.15.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.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swiftbrowser/native",
3
- "version": "0.14.1",
3
+ "version": "0.15.0",
4
4
  "type": "module",
5
5
  "description": "Run a @swiftbrowser/react app natively: the runtime a JavaScriptCore-hosted app needs (timers, console, fetch and the other web globals over the SwiftBrowser Native bridge), the `run` entry that renders into the native SwiftUI renderer, and the bundler that builds app.js for the Xcode target.",
6
6
  "repository": {
@@ -59,8 +59,8 @@
59
59
  },
60
60
  "dependencies": {
61
61
  "@babel/core": "^7.29.7",
62
- "@swiftbrowser/react": "0.14.1",
63
- "@swiftbrowser/web": "0.14.1",
62
+ "@swiftbrowser/react": "0.15.0",
63
+ "@swiftbrowser/web": "0.15.0",
64
64
  "esbuild": "^0.28.2",
65
65
  "react-refresh": "^0.17.0"
66
66
  },
@@ -23,10 +23,14 @@ let package = Package(
23
23
  .library(name: "SwiftBrowserProtocol", targets: ["SwiftBrowserProtocol"]),
24
24
  .library(name: "SwiftBrowserTree", targets: ["SwiftBrowserTree"]),
25
25
  ],
26
+ dependencies: [
27
+ // SVG images, which the system's image decoding does not read (Views/Kinds/RemoteImage.swift).
28
+ .package(url: "https://github.com/swhitty/SwiftDraw.git", .upToNextMinor(from: "0.29.0")),
29
+ ],
26
30
  targets: [
27
31
  .target(name: "SwiftBrowserProtocol"),
28
32
  .target(name: "SwiftBrowserTree", dependencies: ["SwiftBrowserProtocol"]),
29
- .target(name: "SwiftBrowserNative", dependencies: ["SwiftBrowserProtocol", "SwiftBrowserTree"]),
33
+ .target(name: "SwiftBrowserNative", dependencies: ["SwiftBrowserProtocol", "SwiftBrowserTree", .product(name: "SwiftDraw", package: "SwiftDraw", condition: .when(platforms: [.iOS, .macOS]))]),
30
34
  // Apple platforms only (JavaScriptCore): the workers; empty elsewhere.
31
35
  ]
32
36
  )
@@ -156,9 +156,14 @@ struct NavigationScreenElement: View {
156
156
  case "secondary": return .secondaryAction
157
157
  case "bottom": return bottomPlacement
158
158
  case "largeTitle":
159
- // Beside the large title: the shim's own placement; SwiftUI has none, so the bar's trailing edge.
159
+ // In the large title's place: the shim's placement, and SwiftUI's
160
+ // from iOS 26 (it stands in for `navigationTitle`'s text while the
161
+ // title is large). Before that SwiftUI has none: the bar's trailing edge.
160
162
  #if canImport(FoundationBridge)
161
163
  return .largeTitle
164
+ #elseif os(iOS)
165
+ if #available(iOS 26, *) { return .largeTitle }
166
+ return trailingPlacement
162
167
  #else
163
168
  return trailingPlacement
164
169
  #endif
@@ -0,0 +1,88 @@
1
+ #if canImport(SwiftUI)
2
+ import Foundation
3
+ import SwiftUI
4
+ #if canImport(FoundationBridge)
5
+ import FoundationBridge // `AsyncImage`, against the shim
6
+ #endif
7
+ #if canImport(SwiftDraw)
8
+ import SwiftDraw
9
+ #endif
10
+
11
+ /// An `image` with a URL: SwiftUI's `AsyncImage`, and for an SVG, which the
12
+ /// system's image decoding does not read (a logo found on a website often
13
+ /// is one), SwiftDraw's parser and renderer, drawn as vectors at any size.
14
+ /// SwiftDraw is the package's dependency on Apple platforms only; the round
15
+ /// trip's build of these views has the page, which draws SVG itself.
16
+ struct RemoteImage: View {
17
+ let url: URL?
18
+ let resizable: Bool
19
+ let width: Double
20
+ let height: Double
21
+
22
+ var body: some View {
23
+ AsyncImage(url: url) { phase in
24
+ if let image = phase.image {
25
+ if resizable { image.resizable() } else { image }
26
+ } else if phase.error != nil, let url {
27
+ undecoded(url)
28
+ } else {
29
+ placeholder
30
+ }
31
+ }
32
+ }
33
+
34
+ /// Nothing, at the image's size when the app knows it.
35
+ @ViewBuilder private var placeholder: some View {
36
+ if width > 0, height > 0 {
37
+ Color.clear.frame(width: width, height: height)
38
+ } else {
39
+ Color.clear
40
+ }
41
+ }
42
+
43
+ /// What `AsyncImage` could not decode: an SVG, drawn by SwiftDraw, else nothing.
44
+ @ViewBuilder private func undecoded(_ url: URL) -> some View {
45
+ #if canImport(SwiftDraw)
46
+ RemoteSVG(url: url, resizable: resizable) { placeholder }
47
+ #else
48
+ placeholder
49
+ #endif
50
+ }
51
+ }
52
+
53
+ #if canImport(SwiftDraw)
54
+ /// The SVG at `url`, loaded through the shared session (so the cookies and
55
+ /// cache `AsyncImage` used apply), or the placeholder while it loads and
56
+ /// when the data is not an SVG.
57
+ struct RemoteSVG<Placeholder: View>: View {
58
+ let url: URL
59
+ let resizable: Bool
60
+ @ViewBuilder let placeholder: () -> Placeholder
61
+ @State private var loaded: LoadedSVG?
62
+
63
+ var body: some View {
64
+ Group {
65
+ if let loaded, loaded.url == url, let svg = loaded.svg {
66
+ if resizable { SVGView(svg: svg).resizable() } else { SVGView(svg: svg) }
67
+ } else {
68
+ placeholder()
69
+ }
70
+ }
71
+ .task(id: url) {
72
+ loaded = LoadedSVG(url: url, svg: await loadSVG(from: url))
73
+ }
74
+ }
75
+ }
76
+
77
+ struct LoadedSVG: Equatable {
78
+ let url: URL
79
+ let svg: SVG?
80
+ }
81
+
82
+ /// The SVG at `url`, or nil when it cannot be loaded or is not an SVG.
83
+ func loadSVG(from url: URL, session: URLSession = .shared) async -> SVG? {
84
+ guard let data = try? await session.data(from: url).0 else { return nil }
85
+ return SVG(data: data)
86
+ }
87
+ #endif
88
+ #endif
@@ -60,7 +60,8 @@ struct TextElement: View {
60
60
  }
61
61
 
62
62
  /// `image`: an SF Symbol (every name is right here, unlike the web's mapped
63
- /// glyphs), an asset catalog image, a remote image, or a bitmap recipe (not drawn yet).
63
+ /// glyphs), an asset catalog image, a remote image (RemoteImage.swift: SVG
64
+ /// too), or a bitmap recipe (not drawn yet).
64
65
  struct ImageElement: View {
65
66
  let node: ElementNode
66
67
 
@@ -82,15 +83,7 @@ struct ImageElement: View {
82
83
  Image(name)
83
84
  }
84
85
  case .url(let url, let resizable, let width, let height):
85
- AsyncImage(url: URL(string: url)) { phase in
86
- if let image = phase.image {
87
- if resizable { image.resizable() } else { image }
88
- } else if width > 0, height > 0 {
89
- Color.clear.frame(width: width, height: height)
90
- } else {
91
- Color.clear
92
- }
93
- }
86
+ RemoteImage(url: URL(string: url), resizable: resizable, width: width, height: height)
94
87
  case .bitmap(_, _, let width, let height):
95
88
  // A `UIImage` recipe the page would decode; not drawn natively yet.
96
89
  Rectangle().fill(.quaternary).frame(width: width > 0 ? CGFloat(width) : nil, height: height > 0 ? CGFloat(height) : nil)
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: package/wasm/SwiftBrowserNative.wasm
3
- size: 49301069 bytes
4
- sha256: 644cdf56fe487eb95d2c2c0e40782626f6fb70259a3ea40a6b9e11d2c23c401a
3
+ size: 49302313 bytes
4
+ sha256: a2426ea2bd5032a173f7975b94c57b9f331404b419e57e87c5573e7350eac8da