@swiftbrowser/native 0.26.2 → 0.26.4

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 CHANGED
@@ -37,10 +37,10 @@ build against the exact version of the renderer this JavaScript was written
37
37
  for. `swiftbrowser native init` lays a project out that way:
38
38
 
39
39
  ```sh
40
- npx @swiftbrowser/cli native init MyApp # MyApp.xcodeproj, ios/, src/, web/
40
+ npx @swiftbrowser/cli native init MyApp # MyApp.xcodeproj (iOS and macOS), apple/, src/, web/
41
41
  cd MyApp && npm install # React, @swiftbrowser/react, @swiftbrowser/web and this package
42
- npm run build # ios/app.js, the bundle the app ships
43
- open MyApp.xcodeproj # run it on a simulator
42
+ npm run build # apple/app.js, the bundle the app ships
43
+ open MyApp.xcodeproj # run it on a simulator, an iPhone or this Mac
44
44
  npm run dev:web # the same app as a web page
45
45
  ```
46
46
 
@@ -76,8 +76,8 @@ run(<App />);
76
76
  ```
77
77
 
78
78
  ```sh
79
- npx swiftbrowser-native build src/index.tsx --out ../ios/app.js # the bundle Xcode ships
80
- npx swiftbrowser-native dev src/index.tsx --out ../ios/app.js # http://localhost:5173/, Fast Refresh on save
79
+ npx swiftbrowser-native build src/index.tsx --out ../apple/app.js # the bundle Xcode ships
80
+ npx swiftbrowser-native dev src/index.tsx --out ../apple/app.js # http://localhost:5173/, Fast Refresh on save
81
81
  ```
82
82
 
83
83
  On the Swift side, `NativeApp().load()` runs the bundled `app.js` and
@@ -152,7 +152,7 @@ the app's scene phase here (`active`, `inactive`, `background`), as
152
152
  ## Workers
153
153
 
154
154
  ```sh
155
- npx swiftbrowser-native build src/index.tsx --out ../ios/app.js --worker render=src/render.worker.ts
155
+ npx swiftbrowser-native build src/index.tsx --out ../apple/app.js --worker render=src/render.worker.ts
156
156
  ```
157
157
 
158
158
  ```ts
@@ -410,7 +410,7 @@ and a page can register its own element under the same name
410
410
  also from @swiftbrowser/react), so the same element draws on both.
411
411
 
412
412
  Swift that is not a view registers as a function, before the bundle loads
413
- (`swiftbrowser native init` writes an `ios/NativeFunctions.swift` for them):
413
+ (`swiftbrowser native init` writes an `apple/NativeFunctions.swift` for them):
414
414
 
415
415
  ```swift
416
416
  NativeFunctions.register("purchase") { call in // async
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swiftbrowser/native",
3
- "version": "0.26.2",
3
+ "version": "0.26.4",
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": {
@@ -63,8 +63,8 @@
63
63
  },
64
64
  "dependencies": {
65
65
  "@babel/core": "^7.29.7",
66
- "@swiftbrowser/react": "0.26.2",
67
- "@swiftbrowser/web": "0.26.2",
66
+ "@swiftbrowser/react": "0.26.4",
67
+ "@swiftbrowser/web": "0.26.4",
68
68
  "esbuild": "^0.28.2",
69
69
  "react-refresh": "^0.17.0"
70
70
  },
@@ -37,6 +37,8 @@ public final class NativeApp {
37
37
  public private(set) var colorScheme: String?
38
38
  public private(set) var dynamicTypeSize: String?
39
39
  public private(set) var horizontalSizeClass: String?
40
+ /// The platform the app runs on, `ios` or `macos` (docs/notes/macos.md), once the root view has said.
41
+ public private(set) var platform: String?
40
42
  /// URLs the app was opened with before its script could take them (a
41
43
  /// universal link that launched it): delivered once it loads.
42
44
  private var pendingURLs: [URL] = []
@@ -312,17 +314,18 @@ public final class NativeApp {
312
314
  }
313
315
 
314
316
  /// Tells the app the device environment; sent once at load and on change.
315
- public func setEnvironment(colorScheme: String? = nil, dynamicTypeSize: String? = nil, horizontalSizeClass: String? = nil) {
317
+ public func setEnvironment(colorScheme: String? = nil, dynamicTypeSize: String? = nil, horizontalSizeClass: String? = nil, platform: String? = nil) {
316
318
  var changed = false
317
319
  if let colorScheme, colorScheme != self.colorScheme { self.colorScheme = colorScheme; changed = true }
318
320
  if let dynamicTypeSize, dynamicTypeSize != self.dynamicTypeSize { self.dynamicTypeSize = dynamicTypeSize; changed = true }
319
321
  if let horizontalSizeClass, horizontalSizeClass != self.horizontalSizeClass { self.horizontalSizeClass = horizontalSizeClass; changed = true }
322
+ if let platform, platform != self.platform { self.platform = platform; changed = true }
320
323
  if changed { sendEnvironment() }
321
324
  }
322
325
 
323
326
  private func sendEnvironment() {
324
- guard isLoaded, colorScheme != nil || dynamicTypeSize != nil || horizontalSizeClass != nil else { return }
325
- dispatch(.environment(colorScheme: colorScheme, dynamicTypeSize: dynamicTypeSize, horizontalSizeClass: horizontalSizeClass))
327
+ guard isLoaded, colorScheme != nil || dynamicTypeSize != nil || horizontalSizeClass != nil || platform != nil else { return }
328
+ dispatch(.environment(colorScheme: colorScheme, dynamicTypeSize: dynamicTypeSize, horizontalSizeClass: horizontalSizeClass, platform: platform))
326
329
  }
327
330
 
328
331
  /// The scene moved between the foreground, inactive and the background.
@@ -81,4 +81,23 @@ private struct CapsuleGlass: ViewModifier {
81
81
  }
82
82
  }
83
83
  #endif
84
+
85
+ /// `.navigationSubtitle` (macOS 11, iOS 26): the screen's `subtitle`, under
86
+ /// the title in a Mac's title bar. The iOS build's SDK floor is older than
87
+ /// the modifier, so only a Mac (and the shim's round trip) draws it.
88
+ struct NavigationSubtitle: ViewModifier {
89
+ let subtitle: String?
90
+
91
+ func body(content: Content) -> some View {
92
+ #if os(macOS) || canImport(FoundationBridge)
93
+ if let subtitle, !subtitle.isEmpty {
94
+ content.navigationSubtitle(subtitle)
95
+ } else {
96
+ content
97
+ }
98
+ #else
99
+ content
100
+ #endif
101
+ }
102
+ }
84
103
  #endif
@@ -15,6 +15,9 @@ struct ListElement: View {
15
15
  let props = ListProps(props: node.props)
16
16
  let list = selectionList(props)
17
17
  let styled: AnyView = {
18
+ // A `Form` (the shim's and React's mark their list `form`): the
19
+ // system's form, which on a Mac is the columns style.
20
+ if props.form { return AnyView(Form { ListRowsView(children: node.children) }) }
18
21
  switch props.style {
19
22
  case "plain": return AnyView(list.listStyle(.plain))
20
23
  case "sidebar": return AnyView(list.listStyle(.sidebar))
@@ -145,6 +145,7 @@ struct NavigationScreenElement: View {
145
145
  let titled = content
146
146
  .modifier(KeyboardBarInset(toolbars: keyboardToolbars))
147
147
  .navigationTitle(props.title)
148
+ .modifier(NavigationSubtitle(subtitle: props.subtitle))
148
149
  .toolbar { ScreenToolbar(toolbars: shown) }
149
150
  .onAppear {
150
151
  // A pushed screen, appearing: its bar as it is now, until the push has settled.
@@ -84,9 +84,19 @@ public struct NativeRootView: View {
84
84
  app.setEnvironment(
85
85
  colorScheme: colorScheme == .dark ? "dark" : "light",
86
86
  dynamicTypeSize: dynamicTypeSize.protocolName,
87
- horizontalSizeClass: sizeClassName
87
+ horizontalSizeClass: sizeClassName,
88
+ platform: Self.platformName
88
89
  )
89
90
  }
91
+
92
+ /// The platform the app is drawn on (docs/notes/macos.md): the OS, as the protocol names it.
93
+ static var platformName: String {
94
+ #if os(macOS)
95
+ "macos"
96
+ #else
97
+ "ios"
98
+ #endif
99
+ }
90
100
  }
91
101
 
92
102
  /// Presents the first of `presented` over `content` (a sheet, a full-screen
@@ -175,11 +175,14 @@ public struct ListProps: Equatable, Sendable {
175
175
  public var editing: Bool?
176
176
  /// `List(selection:)` over a `Set`: several rows may be selected.
177
177
  public var multiSelect: Bool
178
+ /// A `Form`: drawn as one (on a Mac, the columns style).
179
+ public var form: Bool
178
180
  public init(props: JSONObject) {
179
181
  style = props.string("style") ?? "insetGrouped"
180
182
  refreshable = props.bool("refreshable") ?? false
181
183
  editing = props.bool("editing")
182
184
  multiSelect = props.bool("multiSelect") ?? false
185
+ form = props.bool("form") ?? false
183
186
  }
184
187
  }
185
188
 
@@ -294,6 +297,8 @@ public struct ToggleProps: Equatable, Sendable {
294
297
 
295
298
  public struct NavScreenProps: Equatable, Sendable {
296
299
  public var title: String
300
+ /// `.navigationSubtitle`: under the title in a Mac's title bar.
301
+ public var subtitle: String?
297
302
  /// `automatic`, `large` or `inline`.
298
303
  public var displayMode: String
299
304
  /// 0 for the stack's root screen.
@@ -306,6 +311,7 @@ public struct NavScreenProps: Equatable, Sendable {
306
311
  public var statusBarHidden: Bool
307
312
  public init(props: JSONObject) {
308
313
  title = props.string("title") ?? ""
314
+ subtitle = props.string("subtitle")
309
315
  displayMode = props.string("displayMode") ?? "automatic"
310
316
  depth = props.int("depth") ?? 0
311
317
  toolbarRole = props.string("toolbarRole")
@@ -163,18 +163,21 @@ public struct RendererEvent: Equatable, Sendable {
163
163
  with("registered", id: id, ["name": .string(name), "value": value])
164
164
  }
165
165
 
166
- /// The device appearance, text size or size class (sent once at launch and on change).
166
+ /// The device appearance, text size, size class or platform (`ios` or
167
+ /// `macos`, docs/notes/macos.md; sent once at launch and on change).
167
168
  public static func environment(
168
169
  colorScheme: String? = nil,
169
170
  dynamicTypeSize: String? = nil,
170
171
  horizontalSizeClass: String? = nil,
171
- scenePhase: String? = nil
172
+ scenePhase: String? = nil,
173
+ platform: String? = nil
172
174
  ) -> RendererEvent {
173
175
  var fields: JSONObject = [:]
174
176
  if let colorScheme { fields["colorScheme"] = .string(colorScheme) }
175
177
  if let dynamicTypeSize { fields["dynamicTypeSize"] = .string(dynamicTypeSize) }
176
178
  if let horizontalSizeClass { fields["horizontalSizeClass"] = .string(horizontalSizeClass) }
177
179
  if let scenePhase { fields["scenePhase"] = .string(scenePhase) }
180
+ if let platform { fields["platform"] = .string(platform) }
178
181
  return RendererEvent(type: "environment", fields: fields)
179
182
  }
180
183
 
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: package/wasm/SwiftBrowserNative.wasm
3
- size: 50270516 bytes
4
- sha256: 4ab90fcf8d11104965a65927418d4cf6f47e4595cfc42a018cecb08ee849644e
3
+ size: 50287835 bytes
4
+ sha256: af44f347a82278412c3e963bbb3d47e240c12ec80a8220b5641277010d238984