From 1441520ba01f8c9aae1b70b133920e9092aee47d Mon Sep 17 00:00:00 2001 From: Stephen B <158498287+StephenDev0@users.noreply.github.com> Date: Sat, 12 Jul 2025 23:31:14 -0400 Subject: [PATCH] Add per-app script mapping (#224) * Add per-app script mapping * Add PiP toggle under advanced options * Fix PiP visibility condition --- StikJIT/JSSupport/ScriptListView.swift | 55 +++++++++++++++-------- StikJIT/Views/HomeView.swift | 21 +++++---- StikJIT/Views/InstalledAppsListView.swift | 18 ++++++++ StikJIT/Views/SettingsView.swift | 5 +++ 4 files changed, 72 insertions(+), 27 deletions(-) diff --git a/StikJIT/JSSupport/ScriptListView.swift b/StikJIT/JSSupport/ScriptListView.swift index 1889a8a..abfb1ed 100644 --- a/StikJIT/JSSupport/ScriptListView.swift +++ b/StikJIT/JSSupport/ScriptListView.swift @@ -14,36 +14,53 @@ struct ScriptListView: View { @State private var newFileName = "" @State private var showImporter = false @AppStorage("DefaultScriptName") private var defaultScriptName = "attachDetach.js" + + var onSelectScript: ((URL) -> Void)? = nil var body: some View { NavigationStack { List { Section { ForEach(scripts, id: \.self) { script in - NavigationLink { - ScriptEditorView(scriptURL: script) - } label: { - HStack { - Text(script.lastPathComponent) - .font(.headline) - if defaultScriptName == script.lastPathComponent { - Spacer() - Image(systemName: "star.fill") + if let onSelectScript { + Button { + onSelectScript(script) + } label: { + HStack { + Text(script.lastPathComponent) + .font(.headline) + if defaultScriptName == script.lastPathComponent { + Spacer() + Image(systemName: "star.fill") + } } } - } - .swipeActions(edge: .trailing) { - Button(role: .destructive) { - deleteScript(script) + } else { + NavigationLink { + ScriptEditorView(scriptURL: script) } label: { - Label("Delete", systemImage: "trash") + HStack { + Text(script.lastPathComponent) + .font(.headline) + if defaultScriptName == script.lastPathComponent { + Spacer() + Image(systemName: "star.fill") + } + } } - Button { - saveDefaultScript(script) - } label: { - Label("Set Default", systemImage: "star") + .swipeActions(edge: .trailing) { + Button(role: .destructive) { + deleteScript(script) + } label: { + Label("Delete", systemImage: "trash") + } + Button { + saveDefaultScript(script) + } label: { + Label("Set Default", systemImage: "star") + } + .tint(.blue) } - .tint(.blue) } } } footer: { diff --git a/StikJIT/Views/HomeView.swift b/StikJIT/Views/HomeView.swift index 4609751..03ed864 100644 --- a/StikJIT/Views/HomeView.swift +++ b/StikJIT/Views/HomeView.swift @@ -40,8 +40,9 @@ struct HomeView: View { @State private var pendingBundleIdToEnableJIT : String? = nil @State private var pendingPIDToEnableJIT : Int? = nil @AppStorage("enableAdvancedOptions") private var enableAdvancedOptions = false - + @AppStorage("useDefaultScript") private var useDefaultScript = false + @AppStorage("enablePiP") private var enablePiP = true @State var scriptViewShow = false @AppStorage("DefaultScriptName") var selectedScript = "attachDetach.js" @State var jsModel: RunJSViewModel? @@ -304,7 +305,7 @@ struct HomeView: View { } } .pipify(isPresented: Binding( - get: { useDefaultScript && isProcessing }, + get: { enablePiP && isProcessing }, set: { newValue in isProcessing = newValue } )) { RunJSViewPiP(model: $jsModel) @@ -355,8 +356,8 @@ struct HomeView: View { } ) .onOpenURL { url in - print(url.path()) - if url.host() != "enable-jit" { + print(url.path) + if url.host != "enable-jit" { return } @@ -409,11 +410,12 @@ struct HomeView: View { // but we'll keep it empty to avoid breaking anything } - private func getJsCallback() -> DebugAppCallback? { + private func getJsCallback(for scriptName: String? = nil) -> DebugAppCallback? { + let name = scriptName ?? selectedScript let selectedScriptURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] - .appendingPathComponent("scripts").appendingPathComponent(selectedScript) + .appendingPathComponent("scripts").appendingPathComponent(name) - if !FileManager.default.fileExists(atPath: selectedScriptURL.path()) { + if !FileManager.default.fileExists(atPath: selectedScriptURL.path) { return nil } @@ -438,13 +440,16 @@ struct HomeView: View { LogManager.shared.addInfoLog("Starting Debug for \(bundleID)") DispatchQueue.global(qos: .background).async { + let mapping = UserDefaults.standard.dictionary(forKey: "BundleScriptMap") as? [String: String] + let mappedScript = mapping?[bundleID] + let callback = mappedScript != nil ? getJsCallback(for: mappedScript) : (useDefaultScript ? getJsCallback() : nil) let success = JITEnableContext.shared.debugApp(withBundleID: bundleID, logger: { message in if let message = message { // Log messages from the JIT process LogManager.shared.addInfoLog(message) } - }, jsCallback: useDefaultScript ? getJsCallback() : nil) + }, jsCallback: callback) DispatchQueue.main.async { LogManager.shared.addInfoLog("Debug process completed for \(bundleID)") diff --git a/StikJIT/Views/InstalledAppsListView.swift b/StikJIT/Views/InstalledAppsListView.swift index 4d1c855..5654ed6 100644 --- a/StikJIT/Views/InstalledAppsListView.swift +++ b/StikJIT/Views/InstalledAppsListView.swift @@ -141,6 +141,7 @@ struct AppButton: View { let sharedDefaults: UserDefaults @Environment(\.colorScheme) private var colorScheme + @State private var showScriptPicker = false var body: some View { Button(action: selectApp) { @@ -162,6 +163,17 @@ struct AppButton: View { Button { UIPasteboard.general.string = bundleID } label: { Label("Copy Bundle ID", systemImage: "doc.on.doc") } + Button { + showScriptPicker = true + } label: { + Label("Assign Script", systemImage: "chevron.left.slash.chevron.right") + } + } + .sheet(isPresented: $showScriptPicker) { + ScriptListView { url in + assignScript(url) + showScriptPicker = false + } } } @@ -223,6 +235,12 @@ struct AppButton: View { WidgetCenter.shared.reloadAllTimelines() } + private func assignScript(_ url: URL) { + var mapping = UserDefaults.standard.dictionary(forKey: "BundleScriptMap") as? [String: String] ?? [:] + mapping[bundleID] = url.lastPathComponent + UserDefaults.standard.set(mapping, forKey: "BundleScriptMap") + } + private func loadAppIcon(for bundleID: String) { guard loadAppIconsOnJIT else { return } diff --git a/StikJIT/Views/SettingsView.swift b/StikJIT/Views/SettingsView.swift index 8189fe8..fd335c9 100644 --- a/StikJIT/Views/SettingsView.swift +++ b/StikJIT/Views/SettingsView.swift @@ -13,6 +13,7 @@ struct SettingsView: View { @AppStorage("selectedAppIcon") private var selectedAppIcon: String = "AppIcon" @AppStorage("useDefaultScript") private var useDefaultScript = false @AppStorage("enableAdvancedOptions") private var enableAdvancedOptions = false + @AppStorage("enablePiP") private var enablePiP = true @State private var isShowingPairingFilePicker = false @Environment(\.colorScheme) private var colorScheme @@ -280,6 +281,9 @@ struct SettingsView: View { Toggle("Run Default Script After Connecting", isOn: $useDefaultScript) .foregroundColor(.primary) .padding(.vertical, 6) + Toggle("Picture in Picture", isOn: $enablePiP) + .foregroundColor(.primary) + .padding(.vertical, 6) } } .padding(.vertical, 20) @@ -287,6 +291,7 @@ struct SettingsView: View { .onChange(of: enableAdvancedOptions) { _, newValue in if !newValue { useDefaultScript = false + enablePiP = false } } }