craft-native 0.0.410 → 0.0.411
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
|
@@ -3047,7 +3047,7 @@ function pinCraftNativeDependency(packagePath, craftVersion) {
|
|
|
3047
3047
|
`);
|
|
3048
3048
|
}
|
|
3049
3049
|
// package.json
|
|
3050
|
-
var version = "0.0.
|
|
3050
|
+
var version = "0.0.411";
|
|
3051
3051
|
|
|
3052
3052
|
// bin/cli.ts
|
|
3053
3053
|
var spawnedFrom = process6.env[CRAFT_CLI_SPAWN_MARKER];
|
package/dist/index.cjs
CHANGED
package/dist/index.js
CHANGED
|
@@ -11133,6 +11133,16 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
|
|
|
11133
11133
|
if self.keyboardScreenFrame != nil { self.insetScrollViewsForKeyboard() }
|
|
11134
11134
|
guard let scroll = self.enclosingVerticalScroll(of: textView),
|
|
11135
11135
|
let rect = self.caretRect(of: textView, in: scroll) else { return }
|
|
11136
|
+
// UIKit's text view scrolls this scroll view itself, animated, when
|
|
11137
|
+
// the keyboard finishes appearing. Arriving after an edit and
|
|
11138
|
+
// before this pass, it aims at the caret of the stale layout (the
|
|
11139
|
+
// input's top), and while it runs it carries any offset set here
|
|
11140
|
+
// back toward that target. This pass measured the current layout:
|
|
11141
|
+
// stop that animation so the caret stays where it can be seen. A
|
|
11142
|
+
// finger's drag or fling is the person's, and is left alone.
|
|
11143
|
+
if !scroll.isTracking && !scroll.isDecelerating {
|
|
11144
|
+
scroll.setContentOffset(scroll.contentOffset, animated: false)
|
|
11145
|
+
}
|
|
11136
11146
|
let visible = scroll.bounds.inset(by: scroll.adjustedContentInset)
|
|
11137
11147
|
guard !visible.contains(rect) else { return }
|
|
11138
11148
|
scroll.scrollRectToVisible(rect, animated: false)
|
|
@@ -19,6 +19,8 @@ final class CraftMacScene: NSObject, NSWindowDelegate {
|
|
|
19
19
|
/// Where the close button's top-left corner sits, from the window's top-left (`--traffic-lights 18,18`):
|
|
20
20
|
/// a browser lines the buttons up with its own toolbar row. Nil keeps AppKit's place.
|
|
21
21
|
let trafficLights: CGPoint?
|
|
22
|
+
/// The screen has work to finish before the app quits (`craft.desktop.onQuit`).
|
|
23
|
+
var holdsQuit = false
|
|
22
24
|
init(name: String, route: String, options: [String: Any], application: CraftMacApplication) {
|
|
23
25
|
self.name = name
|
|
24
26
|
runtime = CraftMacRuntime(route: route, params: options["params"] as? [String: Any] ?? [:], network: application.network)
|
|
@@ -69,6 +71,7 @@ final class CraftMacScene: NSObject, NSWindowDelegate {
|
|
|
69
71
|
func windowWillClose(_ notification: Notification) {
|
|
70
72
|
runtime.dispose()
|
|
71
73
|
CraftMacApplication.shared.scenes.removeValue(forKey: name)
|
|
74
|
+
CraftMacApplication.shared.quitReady(scene: name)
|
|
72
75
|
}
|
|
73
76
|
func windowDidBecomeKey(_ notification: Notification) { runtime.send("SCREEN_FOCUS", ["focused": true]) }
|
|
74
77
|
func windowDidResignKey(_ notification: Notification) { runtime.send("SCREEN_FOCUS", ["focused": false]) }
|
|
@@ -99,6 +102,9 @@ final class CraftMacApplication: NSObject, NSApplicationDelegate, UNUserNotifica
|
|
|
99
102
|
var testTimer: Timer?
|
|
100
103
|
var processedActions = Set<String>()
|
|
101
104
|
var appearanceObservation: NSKeyValueObservation?
|
|
105
|
+
/// A quit waiting for screens to finish (`craft.desktop.onQuit`), for at most `quitGrace` seconds.
|
|
106
|
+
private var pendingQuit: (token: String, waiting: Set<String>, timeout: Timer)?
|
|
107
|
+
static let quitGrace: TimeInterval = 3
|
|
102
108
|
|
|
103
109
|
func applicationDidFinishLaunching(_ notification: Notification) {
|
|
104
110
|
installMenus()
|
|
@@ -128,6 +134,34 @@ final class CraftMacApplication: NSObject, NSApplicationDelegate, UNUserNotifica
|
|
|
128
134
|
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool { scenes["main"]?.window.makeKeyAndOrderFront(nil); return true }
|
|
129
135
|
func applicationWillTerminate(_ notification: Notification) { scenes.values.forEach { $0.runtime.dispose() }; testTimer?.invalidate() }
|
|
130
136
|
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { false }
|
|
137
|
+
/// Screens holding the quit hear `craftDesktopQuit` and answer `quitReady` once their work is saved.
|
|
138
|
+
/// The main queue keeps running while AppKit waits, so their timers and requests still complete,
|
|
139
|
+
/// unless `terminate` was called from inside a main-queue block: GCD runs no other main-queue
|
|
140
|
+
/// block until that one returns. So the host never calls it from one, and the timeout is a run
|
|
141
|
+
/// loop timer, which fires either way. A second quit while waiting quits at once.
|
|
142
|
+
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
|
|
143
|
+
let holding = scenes.values.filter(\.holdsQuit)
|
|
144
|
+
guard !holding.isEmpty, pendingQuit == nil else { return .terminateNow }
|
|
145
|
+
let token = UUID().uuidString
|
|
146
|
+
let timeout = Timer(timeInterval: Self.quitGrace, repeats: false) { [weak self] _ in self?.finishQuit(token) }
|
|
147
|
+
RunLoop.main.add(timeout, forMode: .common)
|
|
148
|
+
pendingQuit = (token, Set(holding.map(\.name)), timeout)
|
|
149
|
+
for scene in holding { scene.runtime.send("CRAFT_EVENT", ["event": "craftDesktopQuit", "detail": ["token": token]]) }
|
|
150
|
+
return .terminateLater
|
|
151
|
+
}
|
|
152
|
+
/// A screen finished its quit work, or went away. `token` nil matches the quit in progress.
|
|
153
|
+
func quitReady(scene: String, token: String? = nil) {
|
|
154
|
+
guard var pending = pendingQuit, token == nil || token == pending.token else { return }
|
|
155
|
+
pending.waiting.remove(scene)
|
|
156
|
+
pendingQuit = pending
|
|
157
|
+
if pending.waiting.isEmpty { finishQuit(pending.token) }
|
|
158
|
+
}
|
|
159
|
+
private func finishQuit(_ token: String) {
|
|
160
|
+
guard let pending = pendingQuit, pending.token == token else { return }
|
|
161
|
+
pending.timeout.invalidate()
|
|
162
|
+
pendingQuit = nil
|
|
163
|
+
NSApp.reply(toApplicationShouldTerminate: true)
|
|
164
|
+
}
|
|
131
165
|
func open(name: String, route: String, options: [String: Any]) {
|
|
132
166
|
if let scene = scenes[name] { scene.window.makeKeyAndOrderFront(nil); return }
|
|
133
167
|
scenes[name] = CraftMacScene(name: name, route: route, options: options, application: self)
|
|
@@ -253,6 +287,8 @@ final class CraftMacApplication: NSObject, NSApplicationDelegate, UNUserNotifica
|
|
|
253
287
|
guard let image = filter.outputImage?.transformed(by: CGAffineTransform(scaleX: 8, y: 8)), let cg = CIContext().createCGImage(image, from: image.extent), let data = NSBitmapImageRep(cgImage: cg).representation(using: .png, properties: [:]) else { reply(NSNull()); return }
|
|
254
288
|
reply("data:image/png;base64," + data.base64EncodedString())
|
|
255
289
|
case "setBadge": let count = (args.first as? NSNumber)?.intValue ?? 0; NSApp.dockTile.badgeLabel = count > 0 ? String(count) : nil; reply(true)
|
|
290
|
+
case "holdQuit": scene?.holdsQuit = args.first as? Bool != false; reply(true)
|
|
291
|
+
case "quitReady": if let scene { quitReady(scene: scene.name, token: args.first as? String) }; reply(true)
|
|
256
292
|
default: reply(["error": "Unsupported desktop operation: \(method)"])
|
|
257
293
|
}
|
|
258
294
|
}
|
|
@@ -322,7 +358,8 @@ final class CraftMacApplication: NSObject, NSApplicationDelegate, UNUserNotifica
|
|
|
322
358
|
private func testAction(_ action: [String: Any]) -> [String: Any] {
|
|
323
359
|
let kind = action["action"] as? String ?? ""
|
|
324
360
|
if kind == "command", let id = action["id"] as? String { broadcast(id); return ["ok": true] }
|
|
325
|
-
|
|
361
|
+
// After this result is written, from the run loop rather than a main-queue block (see applicationShouldTerminate).
|
|
362
|
+
if kind == "quit" { NSApp.perform(#selector(NSApplication.terminate(_:)), with: nil, afterDelay: 0); return ["ok": true] }
|
|
326
363
|
if kind == "appearance" {
|
|
327
364
|
NSApp.appearance = NSAppearance(named: action["value"] as? String == "dark" ? .darkAqua : .aqua)
|
|
328
365
|
appearanceChanged(); return ["ok": true]
|