@craft-native/ios 0.0.133 → 0.0.135

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
@@ -505,6 +505,13 @@ await window.craft.restorePurchases();
505
505
  window.craft.setKeepAwake(true); // Prevent screen dimming
506
506
  window.craft.setKeepAwake(false); // Allow screen dimming
507
507
 
508
+ // Screen brightness, 0 to 1: always available (no config flag). It is the
509
+ // device's backlight, so put it back: `brightness` from 'craft-native/mobile'
510
+ // remembers the person's level, restores it, and releases it while the app is
511
+ // in the background.
512
+ const level = await window.craft.getBrightness(); // 0.62
513
+ await window.craft.setBrightness(0.1); // resolves with what the screen reports
514
+
508
515
  // Speech: short spoken cues that duck the person's music instead of stopping
509
516
  // it, and play with the silent switch on. Always available (no config flag).
510
517
  // Resolves when the utterance ends: true if spoken, false if stopped or
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@craft-native/ios",
3
- "version": "0.0.133",
3
+ "version": "0.0.135",
4
4
  "homepage": "https://github.com/craft-native/craft/tree/main/packages/ios#readme",
5
5
  "repository": {
6
6
  "type": "git",
@@ -473,6 +473,10 @@ struct CraftApp: App {
473
473
  CraftTabBarView()
474
474
  CraftSplashView(background: appState.config.resolvedBackgroundColor)
475
475
  }
476
+ // The keyboard covers the tab bar, as it does UIKit's: the
477
+ // stack kept out of the keyboard's way and carried the bar
478
+ // up above it, over the field being typed in.
479
+ .ignoresSafeArea(.keyboard, edges: .bottom)
476
480
  } else {
477
481
  // The page's own chrome, drawn natively over it: the tab
478
482
  // bar the page asks for, and the splash until it is ready.
@@ -483,6 +487,7 @@ struct CraftApp: App {
483
487
  CraftTabBarView()
484
488
  CraftSplashView(background: appState.config.resolvedBackgroundColor)
485
489
  }
490
+ .ignoresSafeArea(.keyboard, edges: .bottom)
486
491
  }
487
492
  }
488
493
  .preferredColorScheme(appState.config.colorScheme)
@@ -2135,6 +2140,15 @@ struct CraftWebView: UIViewRepresentable {
2135
2140
  } else {
2136
2141
  rejectCallback(callbackId, error: "Keep awake is disabled", code: "CAPABILITY_DISABLED")
2137
2142
  }
2143
+ // MARK: - Screen Brightness
2144
+ case "getBrightness":
2145
+ getBrightness(callbackId: callbackId)
2146
+ case "setBrightness":
2147
+ if let level = (body["level"] as? NSNumber)?.doubleValue, level.isFinite {
2148
+ setBrightness(level, callbackId: callbackId)
2149
+ } else {
2150
+ rejectCallback(callbackId, error: "setBrightness was called without the values it needs", code: "INVALID_ARGUMENT")
2151
+ }
2138
2152
  // MARK: - Orientation Lock
2139
2153
  case "lockOrientation":
2140
2154
  if config.enableOrientationLock {
@@ -3097,6 +3111,7 @@ struct CraftWebView: UIViewRepresentable {
3097
3111
  orientationLock: \(config.enableOrientationLock),
3098
3112
  deepLinks: \(config.enableDeepLinks),
3099
3113
  flashlight: true,
3114
+ brightness: true,
3100
3115
  speech: true,
3101
3116
  network: true,
3102
3117
  deviceInfo: true,
@@ -4036,6 +4051,25 @@ struct CraftWebView: UIViewRepresentable {
4036
4051
  });
4037
4052
  },
4038
4053
 
4054
+ // Screen brightness, 0 to 1. Each resolves with the level the
4055
+ // screen then reports.
4056
+ getBrightness: function() {
4057
+ var self = this;
4058
+ var id = 'cb_' + (++this._callbackId);
4059
+ window.webkit.messageHandlers.craft.postMessage({action: 'getBrightness', callbackId: id});
4060
+ return new Promise(function(resolve, reject) {
4061
+ self._callbacks[id] = {resolve: resolve, reject: reject};
4062
+ });
4063
+ },
4064
+ setBrightness: function(level) {
4065
+ var self = this;
4066
+ var id = 'cb_' + (++this._callbackId);
4067
+ window.webkit.messageHandlers.craft.postMessage({action: 'setBrightness', level: level, callbackId: id});
4068
+ return new Promise(function(resolve, reject) {
4069
+ self._callbacks[id] = {resolve: resolve, reject: reject};
4070
+ });
4071
+ },
4072
+
4039
4073
  // Speech synthesis. speak() settles when the utterance ends:
4040
4074
  // true when it was spoken, false when something stopped it.
4041
4075
  // No timeout, because a long sentence at a slow rate can
@@ -6231,6 +6265,25 @@ struct CraftWebView: UIViewRepresentable {
6231
6265
  }
6232
6266
  }
6233
6267
 
6268
+ // MARK: - Screen Brightness
6269
+ //
6270
+ // The device's backlight, 0 to 1. It is system-wide, not the app's:
6271
+ // a page that dims it reads it first and puts it back - when it is
6272
+ // done, and when the app leaves the foreground (@stacksjs/mobile's
6273
+ // screen API does both off the page's visibility).
6274
+ private func getBrightness(callbackId: String?) {
6275
+ DispatchQueue.main.async {
6276
+ self.resolveCallback(callbackId, result: Double(UIScreen.main.brightness))
6277
+ }
6278
+ }
6279
+
6280
+ private func setBrightness(_ level: Double, callbackId: String?) {
6281
+ DispatchQueue.main.async {
6282
+ UIScreen.main.brightness = CGFloat(min(max(level, 0), 1))
6283
+ self.resolveCallback(callbackId, result: Double(UIScreen.main.brightness))
6284
+ }
6285
+ }
6286
+
6234
6287
  // MARK: - Speech Synthesis
6235
6288
  //
6236
6289
  // Native rather than the page's own `speechSynthesis`, which in a
@@ -1793,18 +1793,27 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
1793
1793
  button.accessibilityLabel = spec["accessibilityLabel"] as? String
1794
1794
  if let value = spec["badge"] as? NSNumber, value.intValue > 0 {
1795
1795
  let dot = spec["badgeStyle"] as? String == "dot"
1796
- let badge = UILabel()
1797
- badge.text = dot ? nil : value.intValue > 99 ? "99+" : String(value.intValue)
1798
- badge.font = .systemFont(ofSize: 10, weight: .bold)
1799
- badge.textColor = .white
1800
- badge.textAlignment = .center
1801
- badge.backgroundColor = dot ? .systemRed : .systemBlue
1802
- badge.clipsToBounds = true
1803
1796
  let size: CGFloat = dot ? 9 : 18
1804
- badge.layer.cornerRadius = size / 2
1805
- badge.frame = CGRect(x: 44 - size, y: 0, width: size, height: size)
1806
- badge.isAccessibilityElement = false
1807
- button.addSubview(badge)
1797
+ // The fill is a tinted symbol, not a background colour: on iOS 26
1798
+ // a bar item's custom view keeps its tint but drew a plain
1799
+ // backgroundColor white, so the red unread dot came out white
1800
+ // (invisible in light mode) and the count lost its blue.
1801
+ let fill = UIImageView(image: UIImage(systemName: "circle.fill"))
1802
+ fill.tintColor = dot ? .systemRed : .systemBlue
1803
+ fill.contentMode = .scaleToFill
1804
+ fill.frame = CGRect(x: 44 - size, y: 0, width: size, height: size)
1805
+ fill.isAccessibilityElement = false
1806
+ button.addSubview(fill)
1807
+ if !dot {
1808
+ let count = UILabel(frame: fill.frame)
1809
+ count.text = value.intValue > 99 ? "99+" : String(value.intValue)
1810
+ count.font = .systemFont(ofSize: 10, weight: .bold)
1811
+ count.textColor = .white
1812
+ count.textAlignment = .center
1813
+ count.adjustsFontSizeToFitWidth = true
1814
+ count.isAccessibilityElement = false
1815
+ button.addSubview(count)
1816
+ }
1808
1817
  button.accessibilityValue = "\(value.intValue) unread"
1809
1818
  }
1810
1819
  if let source = spec["image"] as? String, let url = URL(string: source), url.scheme == "https" {