diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 28eedf67..9c41cc1b 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -303,7 +303,11 @@ jobs:
- name: Override frida-swift with local FridaCore.xcframework
run: |
+ # Stay on the same frida-swift revision as Package.swift /
+ # Package.resolved. Cloning tip-of-main regenerates bindings that
+ # no longer match this tree.
git clone https://github.com/frida/frida-swift.git $RUNNER_TEMP/frida-swift
+ git -C $RUNNER_TEMP/frida-swift checkout 467d0360c2cc8ae7db9444316656b4154d22213d
cp -R $RUNNER_TEMP/FridaCore.xcframework $RUNNER_TEMP/frida-swift/
python3 - <<'PY'
import os, re
@@ -344,11 +348,15 @@ jobs:
- name: Generate agent bundle
run: |
+ # Fork PRs do not receive Apple signing secrets. Disable signing so
+ # LumaBundleCompiler still builds without a local codesign identity.
xcodebuild \
-project Luma.xcodeproj \
-scheme AgentBundle \
-configuration Release \
-derivedDataPath build/.derived \
+ CODE_SIGN_IDENTITY=- \
+ CODE_SIGNING_ALLOWED=NO \
build
- name: Build
@@ -602,7 +610,11 @@ jobs:
- name: Override frida-swift with local FridaCore.xcframework
run: |
+ # Stay on the same frida-swift revision as Package.swift /
+ # Package.resolved. Cloning tip-of-main regenerates bindings that
+ # no longer match this tree.
git clone https://github.com/frida/frida-swift.git $RUNNER_TEMP/frida-swift
+ git -C $RUNNER_TEMP/frida-swift checkout 467d0360c2cc8ae7db9444316656b4154d22213d
cp -R $RUNNER_TEMP/FridaCore.xcframework $RUNNER_TEMP/frida-swift/
python3 - <<'PY'
import os, re
@@ -643,11 +655,15 @@ jobs:
- name: Generate agent bundle
run: |
+ # Fork PRs do not receive Apple signing secrets. Disable signing so
+ # LumaBundleCompiler still builds without a local codesign identity.
xcodebuild \
-project Luma.xcodeproj \
-scheme AgentBundle \
-configuration Release \
-derivedDataPath build/.derived-ios \
+ CODE_SIGN_IDENTITY=- \
+ CODE_SIGNING_ALLOWED=NO \
build
- name: Build (unsigned, iOS Simulator)
diff --git a/Luma.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Luma.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
index fd4adabb..7d3bf6f4 100644
--- a/Luma.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/Luma.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -6,7 +6,6 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/frida/frida-swift",
"state" : {
- "branch" : "main",
"revision" : "467d0360c2cc8ae7db9444316656b4154d22213d"
}
},
diff --git a/Luma.xcodeproj/xcshareddata/xcschemes/AgentBundle.xcscheme b/Luma.xcodeproj/xcshareddata/xcschemes/AgentBundle.xcscheme
new file mode 100644
index 00000000..b48e8aca
--- /dev/null
+++ b/Luma.xcodeproj/xcshareddata/xcschemes/AgentBundle.xcscheme
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LumaGtk/Sources/LumaGtk/MainWindow.swift b/LumaGtk/Sources/LumaGtk/MainWindow.swift
index 7b568807..d097387f 100644
--- a/LumaGtk/Sources/LumaGtk/MainWindow.swift
+++ b/LumaGtk/Sources/LumaGtk/MainWindow.swift
@@ -3062,7 +3062,16 @@ final class MainWindow: InstrumentUIHost {
}
private func activateGroupChild(sessionID: UUID, group: SessionSidebarGroup, key: String) {
- groupChildActions[groupChildActionKey(sessionID: sessionID, group: group, key: key)]?()
+ let actionKey = groupChildActionKey(sessionID: sessionID, group: group, key: key)
+ // Browse-all opens a popover; keep that off the ListBox signal stack.
+ if key == "browse" {
+ Task { @MainActor [weak self] in
+ await Task.yield()
+ self?.groupChildActions[actionKey]?()
+ }
+ return
+ }
+ groupChildActions[actionKey]?()
}
private func toggleGroupExpansion(sessionID: UUID, group: SessionSidebarGroup) {
diff --git a/LumaGtk/Sources/LumaGtk/SidebarBrowserPopover.swift b/LumaGtk/Sources/LumaGtk/SidebarBrowserPopover.swift
index 10a278cf..cf152222 100644
--- a/LumaGtk/Sources/LumaGtk/SidebarBrowserPopover.swift
+++ b/LumaGtk/Sources/LumaGtk/SidebarBrowserPopover.swift
@@ -26,8 +26,11 @@ final class SidebarBrowserPopover- {
private var query: String = ""
private var filterTask: Task?
private var refreshGeneration: UInt = 0
+ private var isChoosing = false
- private static var visibleRowLimit: Int { 250 }
+ // Cap first paint so Windows GTK is not forced to allocate a huge ListBox
+ // while the popover is still mapping.
+ private static var visibleRowLimit: Int { 150 }
init(
items: [Item],
@@ -73,7 +76,7 @@ final class SidebarBrowserPopover
- {
key.onKeyPressed { [weak self] _, keyval, _, _ in
MainActor.assumeIsolated {
if Int32(keyval) == Gdk.keyEscape {
- self?.dismiss()
+ self?.scheduleDismiss()
return true
}
return false
@@ -101,7 +104,7 @@ final class SidebarBrowserPopover
- {
MainActor.assumeIsolated { self?.chooseFirstMatch() }
}
searchEntry.onStopSearch { [weak self] _ in
- MainActor.assumeIsolated { self?.dismiss() }
+ MainActor.assumeIsolated { self?.scheduleDismiss() }
}
column.append(child: searchEntry)
@@ -119,7 +122,9 @@ final class SidebarBrowserPopover
- {
let index = Int(row.index)
guard index >= 0, index < self.entries.count else { return }
if case .item(let item) = self.entries[index] {
- self.choose(item)
+ // Defer dismiss/unparent until after row-activated finishes;
+ // tearing the popover down mid-emission faults inside GTK.
+ self.scheduleChoose(item)
}
}
}
@@ -132,9 +137,15 @@ final class SidebarBrowserPopover
- {
self.popover = popover
self.listBox = listBox
- refreshList()
+ // Map the empty popover first, then fill rows on a later turn so the
+ // initial present is not competing with a large ListBox rebuild.
popover.popup()
_ = searchEntry.grabFocus()
+ Task { @MainActor [weak self] in
+ await Task.yield()
+ guard let self, self.popover != nil else { return }
+ self.refreshList()
+ }
}
private func scheduleRefresh() {
@@ -261,18 +272,32 @@ final class SidebarBrowserPopover
- {
private func chooseFirstMatch() {
for entry in entries {
if case .item(let item) = entry {
- choose(item)
+ scheduleChoose(item)
return
}
}
}
- private func choose(_ item: Item) {
- dismiss()
- onChoose(item)
+ private func scheduleChoose(_ item: Item) {
+ guard !isChoosing else { return }
+ isChoosing = true
+ Task { @MainActor [weak self] in
+ await Task.yield()
+ guard let self else { return }
+ self.dismiss()
+ self.onChoose(item)
+ }
+ }
+
+ private func scheduleDismiss() {
+ Task { @MainActor [weak self] in
+ await Task.yield()
+ self?.dismiss()
+ }
}
private func dismiss() {
+ guard popover != nil else { return }
popover?.popdown()
cleanup()
}
@@ -281,7 +306,10 @@ final class SidebarBrowserPopover
- {
filterTask?.cancel()
filterTask = nil
refreshGeneration &+= 1
- popover?.unparent()
+ isChoosing = false
+ if let popover {
+ popover.unparent()
+ }
popover = nil
listBox = nil
retainer = nil
diff --git a/Package.swift b/Package.swift
index 35590897..108e48f1 100644
--- a/Package.swift
+++ b/Package.swift
@@ -60,7 +60,11 @@ let package = Package(
.executable(name: "LumaBundleCompiler", targets: ["LumaBundleCompiler"]),
],
dependencies: [
- .package(url: "https://github.com/frida/frida-swift", branch: "main"),
+ // Pin to the revision recorded in Package.resolved. Floating on
+ // branch main pulled a regenerated Frida API that does not match
+ // this tree (and Apple CI clones tip-of-main when injecting the
+ // local FridaCore.xcframework).
+ .package(url: "https://github.com/frida/frida-swift", revision: "467d0360c2cc8ae7db9444316656b4154d22213d"),
.package(url: "https://github.com/apple/swift-crypto", .upToNextMajor(from: "3.0.0")),
.package(url: "https://github.com/groue/GRDB.swift", .upToNextMajor(from: "7.0.0")),
.package(url: "https://github.com/radareorg/SwiftyR2", branch: "main"),