craft-native 0.0.218 → 0.0.220

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/dist/cli.js CHANGED
@@ -3424,7 +3424,7 @@ function pinCraftNativeDependency(packagePath, craftVersion) {
3424
3424
  `);
3425
3425
  }
3426
3426
  // package.json
3427
- var version = "0.0.218";
3427
+ var version = "0.0.220";
3428
3428
 
3429
3429
  // bin/cli.ts
3430
3430
  var spawnedFrom = process6.env[CRAFT_CLI_SPAWN_MARKER];
package/dist/index.cjs CHANGED
@@ -309,7 +309,7 @@ function craftBinaryNotFoundMessage(triedPath) {
309
309
  `);
310
310
  }
311
311
  // package.json
312
- var version = "0.0.218";
312
+ var version = "0.0.220";
313
313
 
314
314
  // src/sdk-version.ts
315
315
  var SDK_VERSION = version;
package/dist/index.js CHANGED
@@ -57,7 +57,7 @@ function craftBinaryNotFoundMessage(triedPath) {
57
57
  `);
58
58
  }
59
59
  // package.json
60
- var version = "0.0.218";
60
+ var version = "0.0.220";
61
61
 
62
62
  // src/sdk-version.ts
63
63
  var SDK_VERSION = version;
@@ -137,7 +137,12 @@ function serializeSimulatorSteps(steps) {
137
137
  arg = step.state;
138
138
  else if ("direction" in step)
139
139
  arg = step.direction;
140
- else if ("x" in step) {
140
+ else if ("fromX" in step) {
141
+ const coordinates = [step.fromX, step.fromY, step.toX, step.toY];
142
+ if (coordinates.some((value) => number(value) > 1))
143
+ throw new Error("drag coordinates must be between 0 and 1");
144
+ arg = coordinates.join(" ");
145
+ } else if ("x" in step) {
141
146
  if (number(step.x) > 1 || number(step.y) > 1)
142
147
  throw new Error("tapxy coordinates must be between 0 and 1");
143
148
  arg = `${step.x} ${step.y}`;
@@ -27,6 +27,14 @@ export type SimulatorStep = {
27
27
  command: 'swipe';
28
28
  direction: 'up' | 'down' | 'left' | 'right';
29
29
  }
30
+ /** A slow drag in app-relative coordinates, held before release to avoid a flick. */
31
+ | {
32
+ command: 'drag';
33
+ fromX: number;
34
+ fromY: number;
35
+ toX: number;
36
+ toY: number;
37
+ }
30
38
  /** The system back-swipe: a drag in from the left edge (or the right). */
31
39
  | {
32
40
  command: 'edge';
@@ -691,12 +691,18 @@ final class CraftChrome: ObservableObject {
691
691
  }
692
692
  /// A destination beside the tab pill, outside the tab stacks and swipe order.
693
693
  func updateTabAccessory(_ item: [String: Any]?) {
694
- guard let item, let path = item["path"] as? String,
695
- path.hasPrefix("/"), !path.hasPrefix("//"),
696
- let title = item["title"] as? String else { tabAccessory = nil; return }
697
- tabAccessory = Tab(id: path, title: title, symbol: item["symbol"] as? String ?? "message",
698
- badge: (item["badge"] as? String).flatMap { $0.isEmpty ? nil : String($0.prefix(8)) },
699
- accessibilityLabel: item["accessibilityLabel"] as? String)
694
+ let next: Tab?
695
+ if let item, let path = item["path"] as? String,
696
+ path.hasPrefix("/"), !path.hasPrefix("//"),
697
+ let title = item["title"] as? String {
698
+ next = Tab(id: path, title: title, symbol: item["symbol"] as? String ?? "message",
699
+ badge: (item["badge"] as? String).flatMap { $0.isEmpty ? nil : String($0.prefix(8)) },
700
+ accessibilityLabel: item["accessibilityLabel"] as? String)
701
+ } else {
702
+ next = nil
703
+ }
704
+ guard tabAccessory != next else { return }
705
+ tabAccessory = next
700
706
  }
701
707
 
702
708
  func setHiddenByScreen(_ hidden: Bool) {
@@ -936,6 +942,7 @@ struct CraftTabBarView: View {
936
942
  .foregroundStyle(Color.primary)
937
943
  .frame(width: 62, height: 62)
938
944
  }
945
+ .contentShape(Rectangle())
939
946
  .overlay(alignment: .topTrailing) {
940
947
  if let badge = accessory.badge {
941
948
  Text(badge)
@@ -1243,6 +1243,7 @@ final class CraftHybridController: UIViewController, UINavigationControllerDeleg
1243
1243
  target.view.isHidden = true
1244
1244
  target.view.frame = self.view.bounds
1245
1245
  current.view.frame = self.view.bounds
1246
+ (current.topViewController as? CraftNativeScreenController)?.screenVisibilityChanged(true)
1246
1247
  if target.topViewController is CraftHybridWebSlot,
1247
1248
  current.topViewController is CraftNativeScreenController {
1248
1249
  self.parkWeb()
@@ -1272,6 +1273,7 @@ final class CraftHybridController: UIViewController, UINavigationControllerDeleg
1272
1273
  recognizer.isEnabled = true
1273
1274
  }
1274
1275
  CraftChrome.shared.cancelTabSwipe(restoringSelection: restoringSelection)
1276
+ (visibleStack?.topViewController as? CraftNativeScreenController)?.screenVisibilityChanged(true)
1275
1277
  if visibleStack?.topViewController is CraftNativeScreenController { parkWeb() }
1276
1278
  }
1277
1279
 
@@ -4128,8 +4128,7 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
4128
4128
  applyNavigationBarAppearance()
4129
4129
  // Each screen says whether it wants the tab bar, so one that hid it
4130
4130
  // never leaves it hidden for the next.
4131
- CraftChrome.shared.setHiddenByScreen(tabBarHidden)
4132
- CraftChrome.shared.updateTabAccessory(tabAccessory)
4131
+ restoreShellChrome()
4133
4132
  refreshTraitDefaults()
4134
4133
  }
4135
4134
 
@@ -4176,13 +4175,24 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
4176
4175
  }
4177
4176
  }
4178
4177
 
4178
+ private func restoreShellChrome() {
4179
+ guard !isAppScript else { return }
4180
+ CraftChrome.shared.setHiddenByScreen(tabBarHidden)
4181
+ CraftChrome.shared.updateTabAccessory(tabAccessory)
4182
+ }
4183
+
4179
4184
  /// The screen came on screen or went off it: a screen pushed over it was
4180
4185
  /// popped, or the hybrid shell switched to or away from its tab (which
4181
4186
  /// hides a stack rather than moving it, so UIKit says nothing). The first
4182
4187
  /// appearance is the screen's mount, not news; every later one is
4183
4188
  /// `SCREEN_FOCUS { focused: true }`, and going away is `focused: false`.
4184
4189
  func screenVisibilityChanged(_ visible: Bool) {
4185
- guard !runtimeDisposed, visible != reportedVisible else { return }
4190
+ guard !runtimeDisposed else { return }
4191
+ // Hidden tab stacks receive no UIKit appearance callback. A cancelled
4192
+ // preview also restores a screen that never lost focus, so restore its
4193
+ // chrome even when no new SCREEN_FOCUS event is needed.
4194
+ if visible { restoreShellChrome() }
4195
+ guard visible != reportedVisible else { return }
4186
4196
  reportedVisible = visible
4187
4197
  if visible, !hasAppeared {
4188
4198
  hasAppeared = true
@@ -5709,7 +5719,10 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
5709
5719
  }
5710
5720
 
5711
5721
  private func restoreFocus(_ focused: UIView?) {
5712
- guard let focused, focused.window != nil, !focused.isFirstResponder else { return }
5722
+ guard let focused, focused.window != nil, !focused.isFirstResponder,
5723
+ lastFocusedInput === focused, firstResponder(in: rootStack) == nil else { return }
5724
+ // A delayed acknowledgement belongs to the input that scheduled it.
5725
+ // A newer field or an explicit dismissal owns focus now.
5713
5726
  _ = focused.becomeFirstResponder()
5714
5727
  }
5715
5728
 
@@ -37,6 +37,7 @@ import XCTest
37
37
  /// tab <name> tap a tab bar button
38
38
  /// scrollto <text> swipes up (at most 8 times) until text is hittable
39
39
  /// swipe up|down|left|right
40
+ /// drag x1 y1 x2 y2 slow app-relative drag, held 0.3s before release
40
41
  /// audit <name> accessibility audit (element descriptions, Dynamic Type, contrast); logs each issue
41
42
  /// unlabelled logs every hittable button without a label (FAIL if any)
42
43
  /// dismiss [label] a system prompt's button (default Not Now: "Save Password?")
@@ -367,6 +368,18 @@ final class DriverTests: XCTestCase {
367
368
  case "left": app.swipeLeft()
368
369
  default: app.swipeRight()
369
370
  }
371
+ case "drag":
372
+ let parts = arg.split(separator: " ")
373
+ let coordinates = parts.compactMap { Double($0) }
374
+ guard parts.count == 4, coordinates.count == 4,
375
+ coordinates.allSatisfy({ $0.isFinite && (0...1).contains($0) }) else {
376
+ check(false, "drag needs four coordinates between 0 and 1")
377
+ return
378
+ }
379
+ let start = app.coordinate(withNormalizedOffset: CGVector(dx: CGFloat(coordinates[0]), dy: CGFloat(coordinates[1])))
380
+ let end = app.coordinate(withNormalizedOffset: CGVector(dx: CGFloat(coordinates[2]), dy: CGFloat(coordinates[3])))
381
+ start.press(forDuration: 0.05, thenDragTo: end, withVelocity: .slow, thenHoldForDuration: 0.3)
382
+ note("drag \(arg): ok")
370
383
  case "edge":
371
384
  // The system back-swipe: a drag in from the left edge (or the
372
385
  // right one with `edge right`), which a centred swipe never is.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "craft-native",
3
- "version": "0.0.218",
3
+ "version": "0.0.220",
4
4
  "type": "module",
5
5
  "description": "Build desktop apps with web languages - TypeScript SDK for Craft",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",