@craft-native/ios 0.0.467 → 0.0.471

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
@@ -1401,6 +1401,7 @@ function writeShell(output, config) {
1401
1401
  writeSource(join4(output, "Sources", "CraftVoiceRecording.swift"), readFileSync2(join4(TEMPLATES_DIR, "CraftVoiceRecording.swift"), "utf8"));
1402
1402
  writeSource(join4(output, "Sources", "CraftWebAudio.swift"), readFileSync2(join4(TEMPLATES_DIR, "CraftWebAudio.swift"), "utf8"));
1403
1403
  writeSource(join4(output, "Sources", "CraftHybrid.swift"), readFileSync2(join4(TEMPLATES_DIR, "CraftHybrid.swift"), "utf8"));
1404
+ writeSource(join4(output, "Sources", "CraftFramePacingLog.swift"), readFileSync2(join4(TEMPLATES_DIR, "CraftFramePacingLog.swift"), "utf8"));
1404
1405
  writeSource(join4(output, "Sources", "CraftCrashReporter.swift"), readFileSync2(join4(TEMPLATES_DIR, "CraftCrashReporter.swift"), "utf-8").replace(/\{\{CRAFT_VERSION\}\}/g, CRAFT_VERSION));
1405
1406
  writeSource(join4(output, "Sources", "CraftLiveActivities.swift"), readFileSync2(join4(TEMPLATES_DIR, "CraftLiveActivities.swift"), "utf8"));
1406
1407
  writeSource(join4(output, "Shared", "CraftActivityAttributes.swift"), readFileSync2(join4(TEMPLATES_DIR, "CraftActivityAttributes.swift"), "utf8"));
package/dist/index.js CHANGED
@@ -1615,6 +1615,7 @@ function writeShell(output, config) {
1615
1615
  writeSource(join4(output, "Sources", "CraftVoiceRecording.swift"), readFileSync3(join4(TEMPLATES_DIR, "CraftVoiceRecording.swift"), "utf8"));
1616
1616
  writeSource(join4(output, "Sources", "CraftWebAudio.swift"), readFileSync3(join4(TEMPLATES_DIR, "CraftWebAudio.swift"), "utf8"));
1617
1617
  writeSource(join4(output, "Sources", "CraftHybrid.swift"), readFileSync3(join4(TEMPLATES_DIR, "CraftHybrid.swift"), "utf8"));
1618
+ writeSource(join4(output, "Sources", "CraftFramePacingLog.swift"), readFileSync3(join4(TEMPLATES_DIR, "CraftFramePacingLog.swift"), "utf8"));
1618
1619
  writeSource(join4(output, "Sources", "CraftCrashReporter.swift"), readFileSync3(join4(TEMPLATES_DIR, "CraftCrashReporter.swift"), "utf-8").replace(/\{\{CRAFT_VERSION\}\}/g, CRAFT_VERSION));
1619
1620
  writeSource(join4(output, "Sources", "CraftLiveActivities.swift"), readFileSync3(join4(TEMPLATES_DIR, "CraftLiveActivities.swift"), "utf8"));
1620
1621
  writeSource(join4(output, "Shared", "CraftActivityAttributes.swift"), readFileSync3(join4(TEMPLATES_DIR, "CraftActivityAttributes.swift"), "utf8"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@craft-native/ios",
3
- "version": "0.0.467",
3
+ "version": "0.0.471",
4
4
  "homepage": "https://github.com/craft-native/craft/tree/main/packages/ios#readme",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,18 @@
1
+ import Foundation
2
+
3
+ /// The opt-in frame trace shares one append-only file for this app process.
4
+ enum CraftFramePacingLog {
5
+ static func open(in directory: URL) throws -> FileHandle {
6
+ let manager = FileManager.default
7
+ try manager.createDirectory(at: directory, withIntermediateDirectories: true)
8
+ let url = directory.appendingPathComponent("craft-frame-pacing.log")
9
+ if !manager.fileExists(atPath: url.path),
10
+ !manager.createFile(atPath: url.path, contents: nil) {
11
+ throw NSError(domain: NSCocoaErrorDomain, code: NSFileWriteUnknownError,
12
+ userInfo: [NSFilePathErrorKey: url.path])
13
+ }
14
+ let handle = try FileHandle(forWritingTo: url)
15
+ _ = try handle.seekToEnd()
16
+ return handle
17
+ }
18
+ }
@@ -2703,11 +2703,11 @@ final class CraftFramePacing: NSObject {
2703
2703
  private let output = DispatchQueue(label: "craft.frame-pacing", qos: .utility)
2704
2704
  private lazy var file: FileHandle? = {
2705
2705
  guard let caches = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first else { return nil }
2706
- let url = caches.appendingPathComponent("craft-frame-pacing.log")
2707
- if !FileManager.default.fileExists(atPath: url.path) { FileManager.default.createFile(atPath: url.path, contents: nil) }
2708
- let handle = try? FileHandle(forWritingTo: url)
2709
- _ = try? handle?.seekToEnd()
2710
- return handle
2706
+ do { return try CraftFramePacingLog.open(in: caches) }
2707
+ catch {
2708
+ NSLog("[craft frames] trace file unavailable: %@", String(describing: error))
2709
+ return nil
2710
+ }
2711
2711
  }()
2712
2712
 
2713
2713
  private func write(_ line: String) {
@@ -10779,6 +10779,10 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
10779
10779
  // beneath the screen's floating chrome.
10780
10780
  let overflow = style["overflow"] as? String
10781
10781
  view.clipsToBounds = overflow == "hidden"
10782
+ // A single-line editor owns a text viewport. Its private label
10783
+ // can be wider than the control while editing or showing a URL;
10784
+ // inherited visible overflow must not open that viewport.
10785
+ || view is UITextField
10782
10786
  || (view is UIScrollView && overflow != "visible")
10783
10787
  || (view is UILabel && (view.craftRequestedCornerRadius ?? 0) > 0)
10784
10788
  || ((view is UIImageView || view is CraftNativePositionedImageView) && (borderRadius > 0 || hasCornerRadii))
@@ -182,6 +182,30 @@ final class DriverTests: XCTestCase {
182
182
  return nil
183
183
  }
184
184
 
185
+ /// Safari's web view extends behind its browser bars. Those regions accept
186
+ /// touches for the bars, not for page controls reported inside the web view.
187
+ func browserContentFrame(_ frame: CGRect, topBar: CGRect?, bottomBar: CGRect?) -> CGRect {
188
+ var top = frame.minY
189
+ var bottom = frame.maxY
190
+ if let bar = topBar, bar.contains(CGPoint(x: frame.midX, y: bar.midY)), bar.intersects(frame) {
191
+ top = max(top, bar.maxY)
192
+ }
193
+ if let bar = bottomBar, bar.contains(CGPoint(x: frame.midX, y: bar.midY)), bar.intersects(frame) {
194
+ bottom = min(bottom, bar.minY)
195
+ }
196
+ return CGRect(x: frame.minX, y: top, width: frame.width, height: max(0, bottom - top))
197
+ }
198
+
199
+ func scrollViewport() -> CGRect {
200
+ let webView = app.webViews.firstMatch
201
+ let topBar = app.otherElements["TopBrowserBar"]
202
+ let bottomBar = app.toolbars["BottomBrowserToolbar"]
203
+ guard webView.exists, topBar.exists || bottomBar.exists else { return app.frame }
204
+ return browserContentFrame(webView.frame.intersection(app.frame),
205
+ topBar: topBar.exists ? topBar.frame : nil,
206
+ bottomBar: bottomBar.exists ? bottomBar.frame : nil)
207
+ }
208
+
185
209
  /// A short target fits entirely; an oversized target fills the viewport.
186
210
  func visibleScrollFrame(_ target: CGRect, in viewport: CGRect) -> Bool {
187
211
  let visible = target.intersection(viewport)
@@ -192,7 +216,7 @@ final class DriverTests: XCTestCase {
192
216
 
193
217
  func scrollTargetVisible(_ target: XCUIElement?) -> Bool {
194
218
  guard let target, target.exists, target.isHittable,
195
- visibleScrollFrame(target.frame, in: app.frame) else { return false }
219
+ visibleScrollFrame(target.frame, in: scrollViewport()) else { return false }
196
220
  // Hittable alone accepts controls clipped by the scroll view's edge.
197
221
  // Only an ancestor scroll view clips this target, not its siblings.
198
222
  // One that does not overlap the target is not its ancestor, whatever
@@ -220,7 +244,7 @@ final class DriverTests: XCTestCase {
220
244
  /// below the screen while drawing it over the lower half - and it stops
221
245
  /// short of a keyboard whose frame is known.
222
246
  func scrollPage(towardTop: Bool) {
223
- let frame = app.frame
247
+ let frame = scrollViewport()
224
248
  var lower = frame.minY + frame.height * 0.55
225
249
  let keyboard = app.keyboards.firstMatch
226
250
  if keyboard.exists, !keyboard.frame.isEmpty, keyboard.frame.minY < frame.maxY {
@@ -719,7 +743,7 @@ final class DriverTests: XCTestCase {
719
743
  var found = element(containing: arg)
720
744
  var drags = 0
721
745
  while !scrollTargetVisible(found) && drags < 12 {
722
- let towardTop = found.map { !$0.frame.isEmpty && $0.frame.midY < app.frame.midY } ?? false
746
+ let towardTop = found.map { !$0.frame.isEmpty && $0.frame.midY < scrollViewport().midY } ?? false
723
747
  scrollPage(towardTop: towardTop)
724
748
  drags += 1
725
749
  found = element(containing: arg)