Repository: moderato-app/approf Branch: main Commit: 5e317d5ebf25 Files: 105 Total size: 14.8 MB Directory structure: gitextract_pue27tcn/ ├── .github/ │ └── workflows/ │ └── build.yaml ├── .gitignore ├── LICENSE ├── README.md ├── approf/ │ ├── App/ │ │ ├── AppDataSource.swift │ │ ├── AppDelegate.swift │ │ └── AppShortcuts.swift │ ├── App.swift │ ├── Assets.xcassets/ │ │ ├── About.imageset/ │ │ │ └── Contents.json │ │ ├── AccentColor.colorset/ │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── DocIcon.appiconset/ │ │ │ └── Contents.json │ │ ├── Gopple.imageset/ │ │ │ └── Contents.json │ │ └── jobs.imageset/ │ │ └── Contents.json │ ├── Component/ │ │ ├── Buttons.swift │ │ ├── CountDown.swift │ │ ├── FloatTopTrailing.swift │ │ ├── Symbols.swift │ │ ├── Texts.swift │ │ └── effcts/ │ │ ├── GradientBackgroundAnimation.swift │ │ ├── LightsOff.metal │ │ ├── Ripple.metal │ │ └── Ripple.swift │ ├── ContentView.swift │ ├── Exts.swift │ ├── Info.plist │ ├── Preview/ │ │ └── PerviewData.swift │ ├── Preview Content/ │ │ └── Preview Assets.xcassets/ │ │ └── Contents.json │ ├── Process/ │ │ ├── Command.swift │ │ ├── CommandGenerate.swift │ │ ├── README.md │ │ ├── Whatever.swift │ │ └── pprof │ ├── State/ │ │ ├── AppStorage.swift │ │ ├── State.swift │ │ └── WKState.swift │ ├── SwiftUI AppKit/ │ │ ├── Color+Luminance.swift │ │ ├── Color.swift │ │ ├── Date.swift │ │ ├── Delay.swift │ │ ├── File.swift │ │ ├── Hover.swift │ │ ├── Image.swift │ │ ├── Other.swift │ │ ├── Shortcuts.swift │ │ ├── Toolbar.swift │ │ └── Tooltip.swift │ ├── Types/ │ │ ├── Others.swift │ │ ├── PProfBasic.swift │ │ └── PProfPresentation.swift │ ├── View/ │ │ ├── AboutView.swift │ │ ├── AppFeature.swift │ │ ├── DetailFeature.swift │ │ ├── DetailView.swift │ │ ├── DetectingHTTPView.swift │ │ ├── DragNDrop/ │ │ │ ├── DropAndAppendFeature.swift │ │ │ ├── DropAndAppendView.swift │ │ │ ├── DropAndImportFeature.swift │ │ │ ├── DropAndImportView.swift │ │ │ └── ImportView.swift │ │ ├── FailureFeature.swift │ │ ├── IdleFeature.swift │ │ ├── LaunchingFeature.swift │ │ ├── NavigaionView.swift │ │ ├── PProfRowView.swift │ │ ├── Setting/ │ │ │ ├── DefaultGzAppView.swift │ │ │ ├── GraphvizGuideView.swift │ │ │ └── SettingView.swift │ │ ├── ShortcutView.swift │ │ ├── SuccessFeature.swift │ │ ├── SuccessView.swift │ │ ├── UnderTheHood/ │ │ │ ├── ActionButtonView.swift │ │ │ ├── CommandPreviewView.swift │ │ │ ├── FileListView.swift │ │ │ ├── FileRowView.swift │ │ │ ├── ImportView.swift │ │ │ ├── NameFeature.swift │ │ │ ├── NameView.swift │ │ │ ├── NotificationFeature.swift │ │ │ ├── ShortcutsView.swift │ │ │ ├── StatusView.swift │ │ │ ├── TerminalView.swift │ │ │ ├── UTH.swift │ │ │ └── UnderTheHoodView.swift │ │ ├── WebIndicatorView.swift │ │ ├── WelcomeView.swift │ │ └── WkWrapper.swift │ ├── WindowController.swift │ └── pprof.entitlements ├── approf.xcodeproj/ │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm/ │ │ └── Package.resolved │ └── xcshareddata/ │ └── xcschemes/ │ └── approf.xcscheme ├── approfTests/ │ ├── Files/ │ │ ├── Dummy.swift │ │ └── b.pb │ ├── TestAppDropFiles.swift │ ├── TestDropFiles.swift │ ├── TestDropFilesLegacy.swift │ └── TestPlan.xctestplan ├── approfUITests/ │ ├── pprofUITests.swift │ └── pprofUITestsLaunchTests.swift └── ci_scripts/ ├── ci_post_clone.sh └── macros.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/build.yaml ================================================ name: Build on: [ pull_request, workflow_dispatch ] permissions: read-all env: XCODE_PROJECT: "approf.xcodeproj" XCODE_SCHEME: "approf" CODE_SIGN_IDENTITY: "-" BUILD_DIR: "build" XCODE_ARCHIVE: "approf.xcarchive" APP_NAME: "approf.app" EXPORT_OPTIONS_PLIST: "exportOptions.plist" DMG_NAME: "approf" DMG_FILE_NAME: "approf.dmg" jobs: build: runs-on: macos-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Xcode run: | sudo xcode-select -s "/Applications/Xcode_16.app" xcodebuild -version - name: Allow macro run: | defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES - name: Build run: xcodebuild -project "$XCODE_PROJECT" -scheme "$XCODE_SCHEME" archive CODE_SIGN_IDENTITY="$CODE_SIGN_IDENTITY" -archivePath "$BUILD_DIR/$XCODE_ARCHIVE" - name: Export run: | plutil -convert xml1 - -o "$EXPORT_OPTIONS_PLIST" << EOF { "destination": "export", "method": "mac-application" } EOF xcodebuild -exportArchive -archivePath "$BUILD_DIR/$XCODE_ARCHIVE" -exportPath "$BUILD_DIR" -exportOptionsPlist "$EXPORT_OPTIONS_PLIST" - name: Resign App run: codesign --force --deep -s "$CODE_SIGN_IDENTITY" "$BUILD_DIR/$APP_NAME" - name: Make DMG run: hdiutil create -srcdir "$BUILD_DIR" -volname "$DMG_NAME" "$DMG_FILE_NAME" - name: Upload uses: actions/upload-artifact@v4 with: name: Build path: ${{ env.DMG_FILE_NAME }} ================================================ FILE: .gitignore ================================================ # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore ## User settings xcuserdata/ ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) *.xcscmblueprint *.xccheckout ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) build/ DerivedData/ *.moved-aside *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 ## Obj-C/Swift specific *.hmap ## App packaging *.ipa *.dSYM.zip *.dSYM ## Playgrounds timeline.xctimeline playground.xcworkspace # Swift Package Manager # # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. # Packages/ # Package.pins # Package.resolved # *.xcodeproj # # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata # hence it is not needed unless you have added a package configuration file to your project # .swiftpm .build/ # CocoaPods # # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # # Pods/ # # Add this line if you want to avoid checking in source code from the Xcode workspace # *.xcworkspace # Carthage # # Add this line if you want to avoid checking in source code from Carthage dependencies. # Carthage/Checkouts Carthage/Build/ # Accio dependency management Dependencies/ .accio/ # fastlane # # It is recommended to not store the screenshots in the git repo. # Instead, use fastlane to re-generate the screenshots whenever they are needed. # For more information about the recommended setup visit: # https://docs.fastlane.tools/best-practices/source-control/#source-control fastlane/report.xml fastlane/Preview.html fastlane/screenshots/**/*.png fastlane/test_output # Code Injection # # After new code Injection tools there's a generated folder /iOSInjectionProject # https://github.com/johnno1962/injectionforxcode iOSInjectionProject/ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2024 moderato.app Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ https://github.com/user-attachments/assets/d61cd45f-abd8-4fa1-9038-5f9968ce9c9c ## A native macOS app for [pprof](https://github.com/google/pprof) Open pprof profiles without command-line hassle ✨. ## Install ```bash brew install approf ``` You can also download the latest app from [release](https://github.com/moderato-app/approf/releases/latest). ## Requirements * `Graphviz` installed * macOS **Sonoma 14.0** or later on a **M-series chip** _Translucent background is only availble on macOS **Sequoia 15.0** or later_ ## Features - [x] Drag and drop pprof files to open - [x] Compare pprof profiles using the [`-diff_base`](https://github.com/google/pprof/blob/main/doc/README.md#comparing-profiles) option - [x] Reorder / Add / Remove files in seconds - [x] Dark / Light mode - [x] Save sessions for later use ## Screenshots Screenshot
Command line under the hood


Screenshot
WEB page in dark mode
## Implementation * SwiftUI and AppKit as the UI framework. * The Composable Architecture for state management. * Running [pprof](https://github.com/golang/go/tree/master/src/cmd/pprof) binay in a process. ## Credits image Thanks to [Golang Weekly](https://golangweekly.com/latest) for the shoutout! ================================================ FILE: approf/App/AppDataSource.swift ================================================ import ComposableArchitecture struct DateSource{ @MainActor static let store = Store(initialState: AppFeature.State()) { AppFeature() // ._printChanges() } } ================================================ FILE: approf/App/AppDelegate.swift ================================================ import AppKit import CoreSpotlight import Logging var log = Logger(label: "approf") class AppDelegate: NSObject, NSApplicationDelegate { var windowController: CustomWindowController? func application(_ application: NSApplication, open urls: [URL]) { log.info("application open urls: \(urls)") DateSource.store.send(.drop(.onDropEnds(urls))) } func applicationDidFinishLaunching(_ notification: Notification) { log.logLevel = .debug if let window = NSApp.windows.first { windowController = CustomWindowController(window: window) windowController?.showWindow(self) } registerSpotlightSearch() registerSpotlightSearch2() registerSpotlightSearch3() // showTrafficLights(window, false) } func applicationDidUpdate(_ notification: Notification) { // guard let window = NSApp.keyWindow else { return } // // avoid interfering with traffic lights in full screen mode to prevent glitches. // if !window.styleMask.contains(.fullScreen) { // showTrafficLights(window, UIState.shared.mouseInsideWindow) // } } func applicationWillTerminate(_ notification: Notification) { log.info("applicationWillTerminate") DateSource.store.send(.onAppTermination) } } func showTrafficLights(_ window: NSWindow, _ show: Bool) { NSAnimationContext.runAnimationGroup { context in context.duration = 0.5 // Set the animation duration if let closeButton = window.standardWindowButton(.closeButton) { closeButton.animator().alphaValue = show ? 1.0 : 0.0 } if let miniaturizeButton = window.standardWindowButton(.miniaturizeButton) { miniaturizeButton.animator().alphaValue = show ? 1.0 : 0.0 } if let zoomButton = window.standardWindowButton(.zoomButton) { zoomButton.animator().alphaValue = show ? 1.0 : 0.0 } } } private func registerSpotlightSearch() { let userActivity = NSUserActivity(activityType: "the.future.app.approf.approf.alternative") userActivity.title = "approf" userActivity.keywords = ["pprof", "go", "pp"] userActivity.isEligibleForSearch = true userActivity.isEligibleForPublicIndexing = true userActivity.becomeCurrent() } private func registerSpotlightSearch2() { let attributeSet = CSSearchableItemAttributeSet(contentType: UTType.data) attributeSet.title = "approf" attributeSet.contentDescription = "A native macOS app to view pprof profiles" attributeSet.keywords = ["pprof", "go", "pp"] let item = CSSearchableItem(uniqueIdentifier: "0", domainIdentifier: "the.future.app.approf.approf.alternative", attributeSet: attributeSet) CSSearchableIndex.default().indexSearchableItems([item]) } private func registerSpotlightSearch3() { let attributeSet = CSSearchableItemAttributeSet(itemContentType: "application") attributeSet.title = "approf" attributeSet.keywords = ["pprof", "go", "pp"] attributeSet.contentDescription = "A native macOS app to view pprof profiles" let item = CSSearchableItem(uniqueIdentifier: "the.future.app.approf.approf.alternative3", domainIdentifier: nil, attributeSet: attributeSet) CSSearchableIndex.default().indexSearchableItems([item]) { err in if let err = err { log.error("CSSearchableIndex.default().indexSearchableItems err: \(err)") } } } ================================================ FILE: approf/App/AppShortcuts.swift ================================================ import SwiftUI import UniformTypeIdentifiers struct CMD: Commands { @Environment(\.openWindow) var openWindow @ObservedObject var asm = AppStorageManager.shared var body: some Commands { CommandGroup(replacing: CommandGroupPlacement.appInfo) { Button("About pprof") { self.openWindow(id: "about") } } CommandGroup(after: .newItem) { Button("Open") { let urls = selectMultiFiles(utTypes: allowedImportFileTypes) DateSource.store.send(.drop(.onDropEnds(urls))) } .keyboardShortcut("o", modifiers: [.command]) } CommandGroup(replacing: CommandGroupPlacement.sidebar) { Button("Show/Hide Sidebar") { NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil) } .keyboardShortcut("l", modifiers: [.command, .shift]) Button("Show/Hide Sidebar") { NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil) } .keyboardShortcut("s", modifiers: [.command]) Picker(selection: self.$asm.colorScheme, label: Text("Appearance")) { Text("Automatic").tag(AppColorScheme.automatic) Text("Light").tag(AppColorScheme.light) Text("Dark").tag(AppColorScheme.dark) } } } } ================================================ FILE: approf/App.swift ================================================ import ComposableArchitecture import SwiftUI @main struct MyMain { static func main() { if #available(macOS 15.0, *) { MacOS15App.main() } else { MacOS14App.main() } } } @available(macOS 15.0, *) struct MacOS15App: App { @ObservedObject var asm = AppStorageManager.shared @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { Window("approf", id: "main") { if isTesting { EmptyView() } else { ContentView(store: DateSource.store) .toolbar(removing: .title) .toolbarBackgroundVisibility(.hidden, for: .windowToolbar) .containerBackground(asm.materialType.actualMaterial, for: .window) .preferredColorScheme(asm.computedColorScheme) } } .defaultWindowPlacement { _, context in let displayBounds = context.defaultDisplay.visibleRect let size = CGSize(width: displayBounds.width * 0.8, height: displayBounds.height) return WindowPlacement(size: size) } .commands { CMD() } Settings { SettingsView() .toolbar(removing: .title) .toolbarBackgroundVisibility(.hidden, for: .windowToolbar) .containerBackground(.ultraThinMaterial, for: .window) .preferredColorScheme(asm.computedColorScheme) } .windowManagerRole(.associated) Window("About approf", id: "about") { AboutView() .toolbar(removing: .title) .toolbarBackgroundVisibility(.hidden, for: .windowToolbar) .containerBackground(.ultraThinMaterial, for: .window) .windowMinimizeBehavior(.disabled) .windowResizeBehavior(.disabled) .preferredColorScheme(asm.computedColorScheme) } .windowResizability(.contentSize) .restorationBehavior(.disabled) } } struct MacOS14App: App { @ObservedObject var asm = AppStorageManager.shared @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { Window("approf", id: "main") { if isTesting { EmptyView() } else { ContentView(store: DateSource.store) .preferredColorScheme(asm.computedColorScheme) } } .handlesExternalEvents(matching: ["*"]) .commands { CMD() } Settings { SettingsView() .preferredColorScheme(asm.computedColorScheme) } Window("About approf", id: "about") { AboutView() .preferredColorScheme(asm.computedColorScheme) } .windowResizability(.contentSize) } } func shrinkToFit(ideal: CGSize, bounds: CGRect) -> CGSize { let widthRatio = bounds.width / ideal.width let heightRatio = bounds.height / ideal.height let scale = min(widthRatio, heightRatio) return CGSize(width: ideal.width * scale, height: ideal.height * scale) } ================================================ FILE: approf/Assets.xcassets/About.imageset/Contents.json ================================================ { "images" : [ { "filename" : "AboutImage.png", "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: approf/Assets.xcassets/AccentColor.colorset/Contents.json ================================================ { "colors" : [ { "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: approf/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" }, "images" : [ { "scale" : "1x", "filename" : "icon_1024.png", "idiom" : "ios-marketing", "size" : "1024x1024" }, { "idiom" : "iphone", "scale" : "2x", "size" : "20x20", "filename" : "icon_40.png" }, { "size" : "20x20", "idiom" : "iphone", "filename" : "icon_60.png", "scale" : "3x" }, { "scale" : "2x", "size" : "29x29", "idiom" : "iphone", "filename" : "icon_58.png" }, { "scale" : "3x", "size" : "29x29", "idiom" : "iphone", "filename" : "icon_87.png" }, { "scale" : "2x", "size" : "40x40", "idiom" : "iphone", "filename" : "icon_80.png" }, { "size" : "40x40", "idiom" : "iphone", "scale" : "3x", "filename" : "icon_120.png" }, { "filename" : "icon_120.png", "size" : "60x60", "idiom" : "iphone", "scale" : "2x" }, { "size" : "60x60", "idiom" : "iphone", "filename" : "icon_180.png", "scale" : "3x" }, { "size" : "16x16", "filename" : "icon_16.png", "idiom" : "mac", "scale" : "1x" }, { "idiom" : "mac", "filename" : "icon_32.png", "size" : "16x16", "scale" : "2x" }, { "filename" : "icon_32.png", "scale" : "1x", "size" : "32x32", "idiom" : "mac" }, { "scale" : "2x", "filename" : "icon_64.png", "idiom" : "mac", "size" : "32x32" }, { "scale" : "1x", "idiom" : "mac", "size" : "128x128", "filename" : "icon_128.png" }, { "idiom" : "mac", "scale" : "2x", "filename" : "icon_256.png", "size" : "128x128" }, { "filename" : "icon_256.png", "idiom" : "mac", "scale" : "1x", "size" : "256x256" }, { "filename" : "icon_512.png", "idiom" : "mac", "scale" : "2x", "size" : "256x256" }, { "idiom" : "mac", "scale" : "1x", "filename" : "icon_512.png", "size" : "512x512" }, { "idiom" : "mac", "size" : "512x512", "scale" : "2x", "filename" : "icon_1024.png" } ] } ================================================ FILE: approf/Assets.xcassets/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: approf/Assets.xcassets/DocIcon.appiconset/Contents.json ================================================ { "images" : [ { "filename" : "icon_1024.png", "idiom" : "ios-marketing", "scale" : "1x", "size" : "1024x1024" }, { "filename" : "icon_16.png", "idiom" : "mac", "scale" : "1x", "size" : "16x16" }, { "filename" : "icon_32.png", "idiom" : "mac", "scale" : "2x", "size" : "16x16" }, { "filename" : "icon_32.png", "idiom" : "mac", "scale" : "1x", "size" : "32x32" }, { "filename" : "icon_64.png", "idiom" : "mac", "scale" : "2x", "size" : "32x32" }, { "filename" : "icon_128.png", "idiom" : "mac", "scale" : "1x", "size" : "128x128" }, { "filename" : "icon_256.png", "idiom" : "mac", "scale" : "2x", "size" : "128x128" }, { "filename" : "icon_256.png", "idiom" : "mac", "scale" : "1x", "size" : "256x256" }, { "filename" : "icon_512.png", "idiom" : "mac", "scale" : "2x", "size" : "256x256" }, { "filename" : "icon_512.png", "idiom" : "mac", "scale" : "1x", "size" : "512x512" }, { "filename" : "icon_1024.png", "idiom" : "mac", "scale" : "2x", "size" : "512x512" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: approf/Assets.xcassets/Gopple.imageset/Contents.json ================================================ { "images" : [ { "filename" : "Gopple.png", "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: approf/Assets.xcassets/jobs.imageset/Contents.json ================================================ { "images" : [ { "filename" : "jobs.jpeg", "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: approf/Component/Buttons.swift ================================================ import Pow import SwiftUI struct CopyButton: View { let textGenerator: () -> String @State var clicked = false init(textGenerator: @escaping () -> String) { self.textGenerator = textGenerator } var body: some View { Button(action: { let pasteboard = NSPasteboard.general pasteboard.clearContents() pasteboard.setString(textGenerator(), forType: .string) withAnimation { clicked = true } DispatchQueue.main.asyncAfter(deadline: .now() + 2) { withAnimation { clicked = false } } }) { Image(systemName: icon) .opacity(clicked ? 0 : 1) .overlay { if clicked { Image(systemName: "checkmark.circle") .foregroundStyle(.green) .transition( .movingParts.pop(.green) ) } } } .buttonStyle(PlainButtonStyle()) } var icon: String { if #available(macOS 15.0, *) { "document.on.clipboard" } else { "doc.on.clipboard" } } } struct LightsOnButton: View { @Binding var lightsOn: Bool @State var clickedDate: Date? var body: some View { Button(action: { let now = Date() if let lastClicked = clickedDate, now.timeIntervalSince(lastClicked) < 1.5 { // to avoid wkwebview glitch, do nothing if the button was clicked in the past 1.5 second return } withAnimation { lightsOn.toggle() } clickedDate = now }) { LightbulbSlash(lightsOn: $lightsOn) } } } ================================================ FILE: approf/Component/CountDown.swift ================================================ import Combine import SwiftUI class CountDown: ObservableObject { @Published var remainingSeconds: Int private var timer: AnyCancellable? init(_ seconds: Int) { self.remainingSeconds = seconds } func start() { timer = Timer.publish(every: 1.0, on: .main, in: .common) .autoconnect() .sink { [weak self] _ in self?.tick() // Separate the tick logic into a function } } private func tick() { remainingSeconds -= 1 if remainingSeconds <= 0 { destroy() } } func destroy() { timer?.cancel() timer = nil } deinit { destroy() // Ensure the timer is stopped when the object is deallocated } } ================================================ FILE: approf/Component/FloatTopTrailing.swift ================================================ import ComposableArchitecture import SwiftUI struct NotificationView: View { @Bindable var store: StoreOf var body: some View { ZStack { RoundedRectangle(cornerRadius: 10) .fill(.background) HStack(spacing: 7) { Circle() .foregroundStyle(.orange) .frame(width: 10, height: 10) VStack(alignment: .leading, spacing: 2) { Text(store.text) .fontWeight(.bold) HStack { Text("\(Date().formatted(date: .omitted, time: .shortened))") .foregroundStyle(.secondary) .padding(.trailing, 8) Spacer() if let seconds = store.seconds { Text("Disappears in \(seconds) seconds") .foregroundStyle(.secondary) .font(.footnote) } } } } .padding(8) } .fixedSize() .onAppear { store.send(.onAppear) } } } ================================================ FILE: approf/Component/Symbols.swift ================================================ import SwiftUI struct HeartSlash: View { @State var slashed = false var body: some View { Image(systemName: slashed ? "heart.slash.fill" : "heart.fill") .symbolRenderingMode(.multicolor) .contentTransition(.symbolEffect(.replace)) .onAppear(delay: .seconds(0.25)) { withAnimation { slashed.toggle() } } .onDisappear { withAnimation { slashed.toggle() } } } } struct LightbulbSlash: View { @Binding var lightsOn: Bool var body: some View { Image(systemName: lightsOn ? "lightbulb" : "lightbulb.slash") .contentTransition(.symbolEffect(.replace)) } } ================================================ FILE: approf/Component/Texts.swift ================================================ import SwiftUI struct ScrollableTextBox: View where Content: View { var heightLimit: CGFloat? = nil @ViewBuilder var content: () -> Content @State var contentHeight: CGFloat = 10 var body: some View { Group { if let limit = heightLimit { ScrollView { HStack { VStack(alignment: .leading) { content() } Spacer() } .textSelection(.enabled) .onGeometryChange(for: CGFloat.self) { proxy in proxy.size.height } action: { contentHeight = $0 } } .frame(height: min(contentHeight, limit)) } else { ScrollView { HStack { VStack(alignment: .leading) { content() } Spacer() } .textSelection(.enabled) } } } .padding(8) .background( RoundedRectangle(cornerRadius: 10) .fill(.bar) .strokeBorder(.secondary.opacity(0.5), lineWidth: 0.5) ) } } ================================================ FILE: approf/Component/effcts/GradientBackgroundAnimation.swift ================================================ /* Thanks to https://github.com/flawless-code/swiftui-animations/blob/main/SwiftUIAnimations/SwiftUIAnimations/GradientBackgroundAnimation.swift */ import SwiftUI struct GradientBackgroundAnimation: View { @State private var animateGradient: Bool = false @State private var showName: Bool = false @State private var showFullContent: Bool = false private let startColor: Color = .blue private let endColor: Color = .green var body: some View { VStack(spacing: 20) { if showFullContent { Image("jobs") .resizable() .scaledToFit() .clipShape(RoundedRectangle(cornerRadius: 15)) .containerRelativeFrame(.vertical) { v, _ in v * 0.4 } .padding(.top, 40) .padding(.bottom, 40) } else { Image(systemName: "swiftdata") .font(.system(size: 72, weight: .light)) .padding(.top, 80) .padding(.bottom, 64) } Text("Here's to the crazy ones.") .font(.largeTitle).bold() Text("The misfits, the rebels, the troublemakers,") .font(.title) Text("the round pegs in the square holes, the ones who see things differently.") .font(.title) Text("You can quote them, disagree with them, glorify or vilify them. About the only thing you can't do is ignore them. \nBecause they change things - they push the human race forward. And while some may see them as the crazy ones, we see genius. \nBecause the people who are crazy enough to think they can change the world, are the ones who do.") .scaleEffect(showFullContent ? 1 : 0) .opacity(showFullContent ? 1 : 0) Spacer() Text("Steven Paul Jobs (February 24, 1955 – October 5, 2011)") .fontWeight(.thin) .padding(6) .opacity(showName ? 1 : 0) } .frame(maxWidth: .infinity) .foregroundColor(.black) .padding(.horizontal) .multilineTextAlignment(.center) .background { LinearGradient(colors: [startColor, endColor], startPoint: .topLeading, endPoint: .bottomTrailing) .edgesIgnoringSafeArea(.all) .hueRotation(.degrees(animateGradient ? 45 : 0)) .onAppear { withAnimation(.easeInOut(duration: 3).repeatForever(autoreverses: true)) { animateGradient.toggle() } } // .reverseMask { // RoundedRectangle(cornerRadius: 10) // .padding(6) // } } .delayedHover(5) { h in withAnimation { showName = h } } .delayedHover(10) { h in withAnimation(.easeInOut(duration: 1)) { showFullContent = h } } .ignoresSafeArea() .toolbar(.hidden, for: .windowToolbar) } } struct GradientBackgroundAnimation_Previews: PreviewProvider { static var previews: some View { GradientBackgroundAnimation() .ignoresSafeArea() } } ================================================ FILE: approf/Component/effcts/LightsOff.metal ================================================ #include #include using namespace metal; [[stitchable]] half4 lightsOff(float2 pos, half4 color){ return 1 - color; } ================================================ FILE: approf/Component/effcts/Ripple.metal ================================================ /* See the LICENSE.txt file for this sample’s licensing information. Abstract: A shader that applies a ripple effect to a view when using it as a SwiftUI layer effect. */ #include #include using namespace metal; [[ stitchable ]] half4 Ripple( float2 position, SwiftUI::Layer layer, float2 origin, float time, float amplitude, float frequency, float decay, float speed ) { // The distance of the current pixel position from `origin`. float distance = length(position - origin); // The amount of time it takes for the ripple to arrive at the current pixel position. float delay = distance / speed; // Adjust for delay, clamp to 0. time -= delay; time = max(0.0, time); // The ripple is a sine wave that Metal scales by an exponential decay // function. float rippleAmount = amplitude * sin(frequency * time) * exp(-decay * time); // A vector of length `amplitude` that points away from position. float2 n = normalize(position - origin); // Scale `n` by the ripple amount at the current pixel position and add it // to the current pixel position. // // This new position moves toward or away from `origin` based on the // sign and magnitude of `rippleAmount`. float2 newPosition = position + rippleAmount * n; // Sample the layer at the new position. half4 color = layer.sample(newPosition); // Lighten or darken the color based on the ripple amount and its alpha // component. color.rgb += 0.3 * (rippleAmount / amplitude) * color.a; return color; } ================================================ FILE: approf/Component/effcts/Ripple.swift ================================================ /* Copyright © 2024 Apple Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import SwiftUI @available(macOS 15.0, *) #Preview("Ripple") { RPView() } @available(macOS 15.0, *) struct RPView: View { @State var counter: Int = 0 @State var origin: CGPoint = .zero var body: some View { VStack { Spacer() Image("About") .resizable() .aspectRatio(contentMode: .fit) .clipShape(RoundedRectangle(cornerRadius: 24)) .modifier(RippleEffect(at: origin, trigger: counter)) Spacer() } .padding() } } struct PushEffect: ViewModifier { var trigger: T func body(content: Content) -> some View { content.keyframeAnimator( initialValue: 1.0, trigger: trigger ) { view, value in view.visualEffect { view, _ in view.scaleEffect(value) } } keyframes: { _ in SpringKeyframe(0.95, duration: 0.2, spring: .snappy) SpringKeyframe(1.0, duration: 0.2, spring: .bouncy) } } } /// A modifer that performs a ripple effect to its content whenever its /// trigger value changes. struct RippleEffect: ViewModifier { var origin: CGPoint var trigger: T init(at origin: CGPoint, trigger: T) { self.origin = origin self.trigger = trigger } func body(content: Content) -> some View { let origin = origin let duration = duration content.keyframeAnimator( initialValue: 0, trigger: trigger ) { view, elapsedTime in view.modifier(RippleModifier( origin: origin, elapsedTime: elapsedTime, duration: duration )) } keyframes: { _ in MoveKeyframe(0) LinearKeyframe(duration, duration: duration) } } var duration: TimeInterval { 3 } } /// A modifier that applies a ripple effect to its content. struct RippleModifier: ViewModifier { var origin: CGPoint var elapsedTime: TimeInterval var duration: TimeInterval var amplitude: Double = 12 var frequency: Double = 15 var decay: Double = 8 var speed: Double = 1200 func body(content: Content) -> some View { let shader = ShaderLibrary.Ripple( .float2(origin), .float(elapsedTime), // Parameters .float(amplitude), .float(frequency), .float(decay), .float(speed) ) let maxSampleOffset = maxSampleOffset let elapsedTime = elapsedTime let duration = duration content.visualEffect { view, _ in view.layerEffect( shader, maxSampleOffset: maxSampleOffset, isEnabled: elapsedTime > 0 && elapsedTime < duration ) } } var maxSampleOffset: CGSize { CGSize(width: amplitude, height: amplitude) } } ================================================ FILE: approf/ContentView.swift ================================================ import ComposableArchitecture import SwiftUI struct ContentView: View { @Bindable var store: StoreOf @State var uiState = UIState.shared var body: some View { NavigaionView(store: store) .onHover { uiState.mouseInsideWindow = $0 } .overlay { DropAndImportView(store: store.scope(state: \.drop, action: \.drop)) } } } ================================================ FILE: approf/Exts.swift ================================================ import Foundation import Logging import Network extension String: @retroactive Error {} func findRandomAvailablePort() throws -> UInt16 { let parameters = NWParameters.tcp parameters.requiredLocalEndpoint = NWEndpoint.hostPort(host: .ipv4(.loopback), port: .any) let listener = try NWListener(using: parameters) guard let port = listener.port else { throw thr("port is nil") } listener.cancel() return port.rawValue } func createTemporaryDirectory() -> URL? { let tempDirectoryURL = FileManager.default.temporaryDirectory let temporaryDirectoryName = UUID().uuidString let temporaryDirectoryURL = tempDirectoryURL.appendingPathComponent(temporaryDirectoryName, isDirectory: true) do { try FileManager.default.createDirectory(at: temporaryDirectoryURL, withIntermediateDirectories: true, attributes: nil) return temporaryDirectoryURL } catch { print("Error creating temporary directory: \(error)") return nil } } func detectHttpOk(httpUrl: URL, followIndirect: Bool = false, interval: Duration = .seconds(0.1), bodyShouldContain: String? = nil) async throws(Error) { var tried = 0 while true { if tried > 0 { log.debug("detectHttpOk will continue, tried:\(tried)") try await Task.sleep(for: interval) } tried += 1 var d: Data? var r: URLResponse? do { (d, r) = try await URLSession.shared.data(for: URLRequest(url: httpUrl)) } catch { log.debug("detecting http liveness: failed, Unexpected response body: \(error))") continue } let data = d! let response = r! if let httpResponse = response as? HTTPURLResponse { if (200 ... 299).contains(httpResponse.statusCode) { if let text = bodyShouldContain { let dataString = String(data: data, encoding: .utf8) if dataString?.contains(text) ?? false { log.debug("detecting http liveness: success)") return } else { log.debug("detecting http liveness: failed, Unexpected response body: \(String(describing: dataString))") } } else { log.debug("detecting http liveness: success)") return } } else { log.debug("detecting http liveness: failed, Request failed with status code: \(httpResponse.statusCode)") } } try await Task.sleep(for: interval) } } func detectPipeContains(_ pipe: Pipe, _ shouldStartWith: String) async throws(Error) { let handle = pipe.fileHandleForReading var read = "" while let data = try handle.readToEnd(), let output = String(data: data, encoding: .utf8) { read.append(output) if read.count >= shouldStartWith.count { if read.starts(with: shouldStartWith) { log.debug("detecting pipe: success)") return } else { throw thr("Unexpected output: \(read)") } } else { if shouldStartWith.starts(with: read) { // continue to read continue } else { throw thr("Unexpected output: \(read)") } } } throw thr("Unexpected output: \(read)") } extension Logger { func infoString(_ s: String) { self.info("\(s)") } func thr(_ s: String) throws { self.error("\(s)") throw s } func thr(_ e: T) -> T where T: Error { self.error("== Throwing \(e.self) ==") return e } } func thr(_ e: T) -> T where T: Error { log.error("== Throwing \(e.self) ==") return e } func readFirstLine(from fileURL: URL) -> String? { do { let content = try String(contentsOf: fileURL, encoding: .utf8) let lines = content.components(separatedBy: .newlines) return lines.first } catch { print("Error reading file: \(error.localizedDescription)") return nil } } func getAppVersion() -> String { if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String, let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String { return "\(version) (\(build))" } return "" } extension String { // "🇩🇪€4€9".extract("[0-9]") ==> ["4", "9"] func extract(regex: String) -> [String] { do { let regex = try NSRegularExpression(pattern: regex) let results = regex.matches(in: self, range: NSRange(self.startIndex..., in: self)) return results.map { String(self[Range($0.range, in: self)!]) } } catch { log.error("invalid regex: \(error.localizedDescription)") return [] } } func extractPort() -> UInt16? { let pattern = "Serving web UI on http://localhost:(\\d+)" let regex = try! NSRegularExpression(pattern: pattern, options: []) if let match = regex.firstMatch(in: self, options: [], range: NSRange(location: 0, length: self.utf16.count)) { if let range = Range(match.range(at: 1), in: self) { let port = UInt16(self[range]) return port } } return nil } } // How to force SwiftUI Text to ‘break by character // https://medium.com/@Jager-yoo/watchos-how-to-force-swiftui-text-to-break-by-character-cd22526568b8 extension String { /// Forces the string to apply the break by character mode. /// /// Text("This is a long text.".forceCharWrapping) var forceCharWrapping: Self { self.map { String($0) }.joined(separator: "\u{200B}") } } extension String { var asUrl: URL? { URL(string: self) } } extension UInt64 { /// Returns a human-readable file size string (e.g., "18B", "20KB", "20MB"). func humanReadableFileSize() -> String { let size = Double(self) if size < 1024 { return "\(self)B" } let units = ["KB", "MB", "GB", "TB", "PB"] var unitIndex = 0 var adjustedSize = size / 1024.0 while adjustedSize >= 1024 && unitIndex < units.count - 1 { adjustedSize /= 1024 unitIndex += 1 } return String(format: "%.1f%@", adjustedSize, units[unitIndex]) } } func getFileSize(atPath path: String) -> UInt64? { do { let attributes = try FileManager.default.attributesOfItem(atPath: path) if let fileSize = attributes[FileAttributeKey.size] as? UInt64 { return fileSize } } catch { print("Error: \(error)") } return nil } func getFileSize(atURL url: URL) -> UInt64? { return getFileSize(atPath: url.path) } extension String { var fileSize: UInt64? { getFileSize(atPath: self) } } extension URL { func replaceHomeWithTilde() -> String { let homeDirectory = FileManager.default.homeDirectoryForCurrentUser if self.path.hasPrefix(homeDirectory.path) { let relativePath = self.path(percentEncoded: false).replacingOccurrences(of: homeDirectory.path, with: "~") return relativePath } else { return self.path(percentEncoded: false) } } } ================================================ FILE: approf/Info.plist ================================================ CFBundleDocumentTypes CFBundleTypeName public data CFBundleTypeRole Editor LSHandlerRank Default LSItemContentTypes public.data CFBundleTypeName gz CFBundleTypeRole Editor LSHandlerRank Default LSItemContentTypes org.gnu.gnu-zip-archive CFBundleTypeName pb CFBundleTypeRole Editor LSHandlerRank Default LSItemContentTypes the.future.app.pprof.pprof.filetype.pb CFBundleTypeName prof CFBundleTypeRole Editor LSHandlerRank Default LSItemContentTypes the.future.app.pprof.pprof.filetype.prof CFBundleTypeName proff CFBundleTypeRole Editor LSHandlerRank Default LSItemContentTypes the.future.app.pprof.pprof.filetype.proff CFBundleTypeName pprof CFBundleTypeRole Editor LSHandlerRank Default LSItemContentTypes the.future.app.pprof.pprof.filetype.pprof INAlternativeAppNames INAlternativeAppName go INAlternativeAppName pprof NSAppTransportSecurity NSAllowsArbitraryLoads UTExportedTypeDeclarations UTTypeConformsTo public.data UTTypeDescription google pprof file format - pb UTTypeIcons UTTypeIconBadgeName DocIcon UTTypeIconText pprof UTTypeIdentifier the.future.app.pprof.pprof.filetype.pb UTTypeTagSpecification public.filename-extension pb UTTypeConformsTo public.data UTTypeDescription google pprof file format - prof UTTypeIcons UTTypeIconBadgeName DocIcon UTTypeIconText pprof UTTypeIdentifier the.future.app.pprof.pprof.filetype.prof UTTypeTagSpecification public.filename-extension prof UTTypeConformsTo public.data UTTypeDescription google pprof file format - proff UTTypeIcons UTTypeIconBadgeName DocIcon UTTypeIconText pprof UTTypeIdentifier the.future.app.pprof.pprof.filetype.proff UTTypeTagSpecification public.filename-extension proff UTTypeConformsTo public.data UTTypeDescription google pprof file format - pprof UTTypeIcons UTTypeIconBadgeName DocIcon UTTypeIconText pprof UTTypeIdentifier the.future.app.pprof.pprof.filetype.pprof UTTypeTagSpecification public.filename-extension pprof ================================================ FILE: approf/Preview/PerviewData.swift ================================================ import ComposableArchitecture import Foundation extension UnderTheHood { static var mock = Store(initialState: UnderTheHood.State(basic: Shared(PProfBasic( uuid: UUID(), urls: [ URL(fileURLWithPath: "/Library/Application\\ Support/Apple/ParentasdlControls/ALRHelperJobs/pprof.etcd.alloc_objects.alloc_space.inuse_objects.inuse_space.002.pb.gz"), URL(fileURLWithPath: "/Library/Application\\ Support/Apple/ParesdfntalControls/ALRHelperJobs/pprof.etcd.alloc_objects.alloc_space.inuse_objects.inuse_space.002.pb.gz"), URL(fileURLWithPath: "/Library/Application\\ Support/Apple/ParentalControls/ALRHelperJobs/pprof.etcd.alloc_objects.alloc_space.inuse_object3s.inuse_space.002.pb.gz"), URL(fileURLWithPath: "/Library/Application\\ Support/Apple/ParentalControls/ALRHelperJobs/pprof.etcd.alloc_objects.alloc_space.inuse_objects.inuse_space.002.pb.gz"), URL(fileURLWithPath: "/Library/Applicdfgation\\ Support/Apple/ParentalControls/ALRHelperJobs/pprof.etcd.alloc_objects.alloc_space.inuse_objects.inuse_space.002.pb.gz"), URL(fileURLWithPath: "/Library/Applicadfgtion\\ Support/Apple/ParentalControls/ALRHelperJobs/pprof.etcd.alloc_objects.alloc_space.inuse_objects.infgfguse_space.002.pb.gz"), URL(fileURLWithPath: "/Users/clement/Documents/example.txt"), URL(fileURLWithPath: "/Users/username/Documents/example.txt") ], createdAt: Date(), presentation: .dft)))) { UnderTheHood() } } ================================================ FILE: approf/Preview Content/Preview Assets.xcassets/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: approf/Process/Command.swift ================================================ import Foundation // create the process without running it func createPProfProcess(_ args: [CommandLine.CommandArg]) throws(PProfError) -> ([CommandLine.CommandArg], Process, AsyncStream) { let pprofPath = "\(Bundle.main.bundlePath)/Contents/Frameworks/pprof" let process = Process() process.executableURL = URL(fileURLWithPath: pprofPath) process.arguments = args.asProcessArgs() let finalCommandArgs = [CommandLine.CommandArg.filePath(pprofPath)] + args var environment = ProcessInfo.processInfo.environment let additional = AppStorageManager.shared.graphvizBinDir.replacingOccurrences(of: ":", with: "") environment["PATH"] = "\(additional):\(environment["PATH"] ?? "")" log.info("PATH:\(String(describing: environment["PATH"]))") process.environment = environment let pipe = Pipe() process.standardOutput = pipe process.standardError = pipe log.debug("pipe created. fileDescriptor: \(pipe.fileHandleForReading.fileDescriptor)") let terminalStream = AsyncStream { con in let handle = pipe.fileHandleForReading handle.readabilityHandler = { fh in if let output = String(data: fh.availableData, encoding: .utf8) { con.yield(output) } // read only once to get the port or an error, preventing next reading operation from blocking loop con.finish() handle.readabilityHandler = nil } process.terminationHandler = { _ in con.finish() do { try handle.close() } catch { log.error("failed to close pipe: \(error.localizedDescription)") } } } return (finalCommandArgs, process, terminalStream) } func detectHttpOk2(httpUrl: URL, followIndirect: Bool = false, bodyShouldContain: String? = nil) async -> (Bool, HTTPResult) { var d: Data? var r: URLResponse? do { (d, r) = try await URLSession.shared.data(for: URLRequest(url: httpUrl)) } catch { log.debug("detecting http liveness: failed, Unexpected response body: \(error))") return (false, HTTPResult.err(error.localizedDescription)) } let data = d! let response = r! let dataString = String(data: data, encoding: .utf8) ?? "" if let httpResponse = response as? HTTPURLResponse { if (200 ... 299).contains(httpResponse.statusCode) { if let text = bodyShouldContain { if dataString.contains(text) { log.debug("detecting http liveness: success)") return (true, HTTPResult.http(code: httpResponse.statusCode, html: dataString)) } else { let trunc = dataString.prefix(100) log.debug("detecting http liveness: failed, Unexpected response body: \(trunc)") return (false, HTTPResult.http(code: httpResponse.statusCode, html: dataString)) } } else { log.debug("detecting http liveness: success)") return (true, HTTPResult.http(code: httpResponse.statusCode, html: dataString)) } } else { log.debug("detecting http liveness: failed, Request failed with status code: \(httpResponse.statusCode)") return (false, HTTPResult.http(code: httpResponse.statusCode, html: dataString)) } } return (false, HTTPResult.err("response is not of type HTTPURLResponse, data: \(dataString)")) } ================================================ FILE: approf/Process/CommandGenerate.swift ================================================ import Foundation extension CommandLine { enum CommandArg: Equatable { case string(String), url(URL), filePath(String), newLine } static func commandPreview(_ presentation: PProfPresentation, _ filePaths: [String]) -> [CommandArg] { var args = commandArgs(presentation, filePaths) args.insert(.string("go tool pprof"), at: 0) return args } static func commandPreview(_ presentation: PProfPresentation, _ urls: [URL]) -> [CommandArg] { let filePahts = urls.map { $0.path(percentEncoded: false) } var args = commandArgs(presentation, filePahts) args.insert(.string("go tool pprof"), at: 0) return args } static func commandArgs(_ presentation: PProfPresentation, _ urls: [URL]) -> [CommandArg] { let filePahts = urls.map { $0.path(percentEncoded: false) } return commandArgs(presentation, filePahts) } static func commandArgs(_ presentation: PProfPresentation, _ filePaths: [String]) -> [CommandArg] { var preview: [CommandArg] = [] preview.append(.string("-http=:")) preview.append(.string("-no_browser")) switch presentation { case .dft: preview.append(.filePath(filePaths.first ?? "")) case .acc: preview.append(.newLine) for fp in filePaths { preview.append(.filePath(fp)) preview.append(.newLine) } if case .newLine = preview.last { preview.removeLast() } case .diff: preview.append(.string("-diff_base")) preview.append(.newLine) for fp in filePaths { preview.append(.filePath(fp)) preview.append(.newLine) } if case .newLine = preview.last { preview.removeLast() } } return preview } } extension [CommandLine.CommandArg] { func asProcessArgs() -> [String] { var args: [String] = [] for arg in self { switch arg { case let .string(a), let .filePath(a): args.append(a) case let .url(a): args.append(a.path(percentEncoded: false)) case .newLine: continue } } return args } func asCopiable() -> String { return self.map { arg in switch arg { case let .string(a): a.trimmingCharacters(in: .whitespacesAndNewlines) case let .url(a): a.path(percentEncoded: false).replacingOccurrences(of: " ", with: "\\ ") case let .filePath(a): a.trimmingCharacters(in: .whitespacesAndNewlines).replacingOccurrences(of: " ", with: "\\ ") case .newLine: "\\\n" } }.joined(separator: " ") } func asPrintable() -> String { return self.map { arg in switch arg { case let .string(a): a.trimmingCharacters(in: .whitespacesAndNewlines) case let .url(a): a.path(percentEncoded: false) case let .filePath(a): a.trimmingCharacters(in: .whitespacesAndNewlines) case .newLine: "\\\n" } }.joined(separator: " ").forceCharWrapping } } ================================================ FILE: approf/Process/README.md ================================================ * pprof: /opt/homebrew/Cellar/go/1.22.4/libexec/pkg/tool/darwin_arm64/pprof ================================================ FILE: approf/Process/Whatever.swift ================================================ import Foundation func dotExist(_ dirPath: String) -> (String, Bool) { let dotFilePath = URL(fileURLWithPath: dirPath).appending(component: "dot").path(percentEncoded: false) return (dotFilePath, FileManager.default.fileExists(atPath: dotFilePath)) } ================================================ FILE: approf/Process/pprof ================================================ [File too large to display: 14.6 MB] ================================================ FILE: approf/State/AppStorage.swift ================================================ import SwiftUI class AppStorageManager: ObservableObject { static var shared = AppStorageManager() // Appearance @AppStorage("colorScheme") var colorScheme = AppColorScheme.dark @AppStorage("lightsOn") var lightsOn = true @AppStorage("materialType") var materialType = MaterialType.ultraThin // Function @AppStorage("graphvizBinDir") var graphvizBinDir = "/opt/homebrew/bin" @AppStorage("selectedSettingsTab") var selectedSettingsTab = SettingsTab.appearance } enum AppColorScheme: String, CaseIterable, Codable { case automatic = "Automatic" case light = "Light" case dark = "Dark" } extension AppStorageManager { var computedColorScheme: ColorScheme? { switch colorScheme { case .light: return ColorScheme.light case .dark: return ColorScheme.dark case .automatic: return nil } } } enum MaterialType: String, CaseIterable, Codable { case ultraThin = "UltraThin", thin = "Thin", regular = "Regular", thick = "Thick", ultraThick = "UltraThick", bar = "Bar" var actualMaterial: Material { switch self { case .regular: return Material.regular case .thick: return Material.thick case .thin: return Material.thin case .ultraThin: return Material.ultraThin case .ultraThick: return Material.ultraThick case .bar: return Material.bar } } } ================================================ FILE: approf/State/State.swift ================================================ import Foundation @Observable class UIState { static var shared = UIState() private init() {} var mouseInsideWindow = true } ================================================ FILE: approf/State/WKState.swift ================================================ import Foundation @Observable class WKState { var url: URL? var loading = false } ================================================ FILE: approf/SwiftUI AppKit/Color+Luminance.swift ================================================ import SwiftUI extension Color { func luminance() -> Double { // Convert SwiftUI Color to CGColor guard let cgColor = self.cgColor else { return 0.0 // Default luminance if conversion fails } // Extract RGB values let components = cgColor.components let red = components?[0] ?? 0.0 let green = components?[1] ?? 0.0 let blue = components?[2] ?? 0.0 // Compute luminance. return 0.2126 * Double(red) + 0.7152 * Double(green) + 0.0722 * Double(blue) } func isLight() -> Bool { return luminance() > 0.5 } func adaptedTextColor() -> Color { return isLight() ? Color.black : Color.white } } ================================================ FILE: approf/SwiftUI AppKit/Color.swift ================================================ import SwiftUI struct AdaptiveForegroundColorModifier: ViewModifier { var lightModeColor: Color var darkModeColor: Color @Environment(\.colorScheme) private var colorScheme func body(content: Content) -> some View { content.foregroundStyle(resolvedColor) } private var resolvedColor: Color { switch colorScheme { case .light: return lightModeColor case .dark: return darkModeColor @unknown default: return lightModeColor } } } extension View { // func foregroundColor( // light lightModeColor: Color, // dark darkModeColor: Color // ) -> some View { // modifier(AdaptiveForegroundColorModifier( // lightModeColor: lightModeColor, // darkModeColor: darkModeColor // )) // } } struct AdaptiveBackgroundColorModifier: ViewModifier { var lightModeColor: Color var darkModeColor: Color @Environment(\.colorScheme) private var colorScheme func body(content: Content) -> some View { content.backgroundStyle(resolvedColor) } private var resolvedColor: Color { switch colorScheme { case .light: return lightModeColor case .dark: return darkModeColor @unknown default: return lightModeColor } } } extension View { func backgroundColor( light lightModeColor: Color, dark darkModeColor: Color ) -> some View { modifier(AdaptiveBackgroundColorModifier( lightModeColor: lightModeColor, darkModeColor: darkModeColor )) } } ================================================ FILE: approf/SwiftUI AppKit/Date.swift ================================================ // Created for approf in 2024 import Foundation extension Date { func isToday() -> Bool { return Calendar.current.isDateInToday(self) } func isYesterday() -> Bool { return Calendar.current.isDateInYesterday(self) } func humanReadable(_ includeTime: Bool = true) -> String { var text = "" if isToday() { text = "Today" } else if isYesterday() { text = "Yesterday" } else { text = formatted(date: .abbreviated, time: .omitted) } if includeTime { return text + " " + formatted(date: .omitted, time: .shortened) } return text } } ================================================ FILE: approf/SwiftUI AppKit/Delay.swift ================================================ import SwiftUI public extension View { func onAppear(delay: Duration, action: @escaping () -> Void) -> some View { task { do { try await Task.sleep(for: delay) } catch { // Task canceled return } await MainActor.run { action() } } } func task(delay: Duration, action: @escaping () -> Void) -> some View { task { do { try await Task.sleep(for: delay) } catch { // Task canceled return } await MainActor.run { action() } } } } ================================================ FILE: approf/SwiftUI AppKit/File.swift ================================================ import SwiftUI import UniformTypeIdentifiers func selectFolder(_ filePath: String?) -> String? { let panel = NSOpenPanel() if let filePath = filePath { panel.directoryURL = URL(filePath: filePath) } panel.allowedContentTypes = [.folder] panel.canChooseFiles = false panel.canChooseDirectories = true panel.allowsMultipleSelection = false if panel.runModal() == .OK { return panel.url?.path } return nil } func selectMultiFiles(_ filePath: String? = nil, utTypes: [UTType]? = nil) -> [URL] { let panel = NSOpenPanel() if let filePath = filePath { panel.directoryURL = URL(filePath: filePath) } if let utTypes = utTypes { panel.allowedContentTypes = utTypes } panel.canChooseFiles = true panel.allowsMultipleSelection = true panel.canChooseDirectories = false if panel.runModal() == .OK { return panel.urls } return [] } extension URL { // doesn't encode; remove last '/' var nicePath: String { let path = self.path(percentEncoded: false) if path.hasSuffix("/") { return String(path.dropLast()) } else { return path } } } ================================================ FILE: approf/SwiftUI AppKit/Hover.swift ================================================ import Combine import Foundation import SwiftUI struct DelayedHoverModifier: ViewModifier { let delay: Double @State private var hovering = false @State private var timerSubscription: AnyCancellable? let onHoverAction: (Bool) -> Void func body(content: Content) -> some View { content .onHover { isHovering in if isHovering { startTimer() } else { stopTimer() onHoverAction(false) } } } private func startTimer() { timerSubscription = Timer.publish(every: delay, on: .main, in: .common).autoconnect().sink { _ in hovering = true onHoverAction(true) stopTimer() } } private func stopTimer() { timerSubscription?.cancel() timerSubscription = nil } } extension View { func delayedHover(_ delay: Double = 1, perform action: @escaping (Bool) -> Void) -> some View { modifier(DelayedHoverModifier(delay: delay, onHoverAction: action)) } } ================================================ FILE: approf/SwiftUI AppKit/Image.swift ================================================ import SwiftUI import AppKit struct RotatingSF: View { let systemName: String let speed: Double @State var degreesRotating = 0.0 init(_ systemName: String, _ speed: Double = 4.0) { self.systemName = systemName self.speed = speed } var body: some View { Image(systemName: systemName) .rotationEffect(.degrees(degreesRotating)) .onAppear { degreesRotating = 0.0 Task { @MainActor in withAnimation(.linear(duration: speed).speed(speed).repeatForever(autoreverses: false)) { degreesRotating = 360.0 } } } } } extension NSImage { /** Resizes the image to the given size. */ func resize(withSize targetSize: NSSize) -> NSImage { let newImage = NSImage(size: targetSize) newImage.lockFocus() draw(in: CGRect(origin: .zero, size: targetSize), from: CGRect(origin: .zero, size: size), operation: .sourceOver, fraction: 1.0) newImage.unlockFocus() return newImage } /** Resizes the image to the given size maintaining its original aspect ratio. */ func resizeMaintainingAspectRatio(withSize targetSize: NSSize) -> NSImage { let newSize: NSSize let widthRatio = targetSize.width / size.width let heightRatio = targetSize.height / size.height if(widthRatio > heightRatio) { newSize = NSSize(width: floor(size.width * widthRatio), height: floor(size.height * widthRatio)) } else { newSize = NSSize(width: floor(size.width * heightRatio), height: floor(size.height * heightRatio)) } return resize(withSize: newSize) } } ================================================ FILE: approf/SwiftUI AppKit/Other.swift ================================================ import SwiftUI extension View { @ViewBuilder func onWidthChange(_ action: @escaping (CGFloat) -> Void) -> some View { self .background( GeometryReader { reader in Color.clear .onChange(of: reader.frame(in: .global).width) { _, newValue in action(newValue) } } ) } } extension View { static func printChagesWhenDebug() { #if DEBUG _printChanges() #endif } } extension View { @ViewBuilder func `if`(_ condition: Bool, transform: (Self) -> Content) -> some View { if condition { transform(self) } else { self } } } extension View { @ViewBuilder func apply(transform: (Self) -> Content) -> some View { transform(self) } } func showInFinder(_ path: String) { let url = URL(fileURLWithPath: path) if url.isFileURL { NSWorkspace.shared.activateFileViewerSelecting([url]) } } func showInFinder(_ url: URL) { if url.isFileURL { NSWorkspace.shared.activateFileViewerSelecting([url]) } } public extension View { @inlinable func reverseMask( alignment: Alignment = .center, @ViewBuilder _ mask: () -> Mask ) -> some View { self.mask { Rectangle() .overlay(alignment: alignment) { mask() .blendMode(.destinationOut) } } } } ================================================ FILE: approf/SwiftUI AppKit/Shortcuts.swift ================================================ import SwiftUI extension View { /// Add a shortcut to an hidden button func sc(_ key: KeyEquivalent, modifiers: EventModifiers = .command, action: @escaping () -> Void) -> some View { overlay { Button("") { action() } .buttonStyle(.plain) .allowsHitTesting(false) .hidden() .keyboardShortcut(key, modifiers: modifiers) } } /// Sometimes, the hidden view doesn’t get created. Use an ID to fix this. func addHiddenView(_ id: ID, @ViewBuilder content: () -> some View) -> some View where ID: Hashable { self.background { content().frame(width: 1, height: 1).opacity(0).allowsTightening(false).id(id) } } } ================================================ FILE: approf/SwiftUI AppKit/Toolbar.swift ================================================ import SwiftUI struct SidebarButton: View { var body: some View { Button(action: toggleSidebar, label: { Image(systemName: "sidebar.leading") }) } private func toggleSidebar() { NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil) } } ================================================ FILE: approf/SwiftUI AppKit/Tooltip.swift ================================================ import Combine import SwiftUI struct TooltipModifier: ViewModifier { let delay: Double let arrowEdge: Edge @ViewBuilder let tooltipContent: () -> TooltipContent @State private var hovering = false @State private var timerSubscription: AnyCancellable? @State private var isPresented = false func body(content: Content) -> some View { content .onHover { isHovering in if isHovering { startTimer() } else { stopTimer() isPresented = false } } .popover(isPresented: $isPresented, arrowEdge: arrowEdge) { tooltipContent() } } private func startTimer() { timerSubscription = Timer.publish(every: delay, on: .main, in: .common).autoconnect().sink { _ in isPresented = true stopTimer() } } private func stopTimer() { timerSubscription?.cancel() timerSubscription = nil } } extension View { func tooltip(delay: Double = 1, arrowEdge: Edge = .top, @ViewBuilder content: @escaping () -> T) -> some View { modifier(TooltipModifier(delay: delay, arrowEdge: arrowEdge, tooltipContent: content)) } } ================================================ FILE: approf/Types/Others.swift ================================================ import UniformTypeIdentifiers let allowedImportFileTypes = [ UTType.data ] enum PProfError: Error { case ordinay(String) } struct TerminalRecord: Equatable { let date: Date let std: Std let text: String enum Std { case out, err } } struct TrakcingProcess: Identifiable, Equatable { var process: Process var id: Int32 { process.processIdentifier } } ================================================ FILE: approf/Types/PProfBasic.swift ================================================ import Foundation struct PProfBasic: Equatable, Identifiable, Codable { var id: UUID var name: String var filePaths: [String] var createdAt: Date var presentation: PProfPresentation var httpDetectLog: [HTTPResult] var terminalOutput: [TerminalRecord] var finalCommandArgs: [CommandLine.CommandArg] init(uuid: UUID, filePaths: [String], createdAt: Date, presentation: PProfPresentation = .dft, httpDetectLog: [HTTPResult] = [], terminalOutput: [TerminalRecord] = [], finalCommandArgs: [CommandLine.CommandArg] = []) { self.id = uuid self.name = "" self.filePaths = filePaths self.createdAt = createdAt self.presentation = presentation self.httpDetectLog = httpDetectLog self.terminalOutput = terminalOutput self.finalCommandArgs = finalCommandArgs } init(uuid: UUID, urls: [URL], createdAt: Date, presentation: PProfPresentation = .dft) { let filePaths = urls.map { $0.path(percentEncoded: false) } self.init(uuid: uuid, filePaths: filePaths, createdAt: createdAt, presentation: presentation) } // Custom encoding func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: .id) try container.encode(self.name, forKey: .name) try container.encode(self.name, forKey: .name) try container.encode(self.filePaths, forKey: .filePaths) try container.encode(self.createdAt, forKey: .createdAt) try container.encode(self.presentation, forKey: .presentation) } // Custom decoding init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decode(UUID.self, forKey: .id) self.name = try container.decodeIfPresent(String.self, forKey: .name) ?? "" self.filePaths = try container.decode([String].self, forKey: .filePaths) self.createdAt = try container.decode(Date.self, forKey: .createdAt) self.presentation = try container.decode(PProfPresentation.self, forKey: .presentation) self.httpDetectLog = [] self.terminalOutput = [] self.finalCommandArgs = [] } // Define coding keys enum CodingKeys: String, CodingKey { case id case name case filePaths case createdAt case presentation } } extension PProfBasic { static let mock = PProfBasic( uuid: UUID(), filePaths: ["/Users/mark/projects/mark/pprof"], createdAt: Date(), httpDetectLog: [ HTTPResult.http(code: 200, html: ""), HTTPResult.http(code: 403, html: ""), ], terminalOutput: [ .init(date: Date.now, std: .err, text: "remote: Enumerating objects: 216520, done."), .init(date: Date.now, std: .out, text: "Resolving deltas: 100% (195133/195133), done.."), ], finalCommandArgs: [.string("pprof"), .string("-http=:"), .filePath("/Users/mark/projects/mark/pprof")] ) } extension PProfBasic { var computedName: String { if !self.name.isEmpty { return self.name } guard let firstFile = self.filePaths.first else { return "No file" } guard let firstFileName = firstFile.asUrl?.lastPathComponent else { return "No file" } return firstFileName } func equalsSnapshot(_ snapshot: Self) -> Bool { snapshot.filePaths == self.filePaths && snapshot.presentation == self.presentation } } extension PProfBasic { func commandArgs() -> [CommandLine.CommandArg] { CommandLine.commandArgs(self.presentation, self.filePaths) } func commandPreview() -> [CommandLine.CommandArg] { CommandLine.commandPreview(self.presentation, self.filePaths) } } ================================================ FILE: approf/Types/PProfPresentation.swift ================================================ enum PProfPresentation: String, Codable, CaseIterable { case dft = "Isolated", acc = "Accumulate", diff = "Diff" var explanation: String { switch self { case .dft: "Run `go tool pprof` on each file separately" case .acc: "Run `go tool pprof` on all files at once" case .diff: "Run `go tool pprof` on all files at once, comparing them to the base" } } var next: PProfPresentation { var nextIndex = Self.allCases.firstIndex(of: self)! + 1 if nextIndex == Self.allCases.count { nextIndex = 0 } return Self(rawValue: Self.allCases[nextIndex].rawValue)! } var prev: PProfPresentation { var nextIndex = Self.allCases.firstIndex(of: self)! - 1 if nextIndex < 0 { nextIndex = Self.allCases.count - 1 } return Self(rawValue: Self.allCases[nextIndex].rawValue)! } } ================================================ FILE: approf/View/AboutView.swift ================================================ import SwiftUI struct AboutView: View { var body: some View { HStack(spacing: 50) { Image("About") .resizable() .scaledToFit() VStack(alignment: .leading) { VStack(alignment: .leading){ Text("approf") .font(.system(size: 40)) .fontWeight(.semibold) Spacer().frame(height: 5) Text("Version \(getAppVersion())") .font(.headline) .fontWeight(.regular) } .offset(y: -15) Spacer() Text("Copyright © 2024 https://github.com/moderato-app/approf") .font(.footnote) } .padding(.bottom, 5) } .frame(height: 100) .padding(40) } } #Preview { AboutView() } ================================================ FILE: approf/View/AppFeature.swift ================================================ import ComposableArchitecture import SwiftUI @Reducer struct AppFeature { @ObservableState struct State: Equatable { var synced = false // whether pprofs is synced with basics @Shared(.approf) var basics: IdentifiedArrayOf = .init(uniqueElements: []) var pprofs: IdentifiedArrayOf = .init() var pprofsSelectedId: UUID? var drop: DropAndImportFeature.State = .init() } enum Action { case onAppear case onPprofsSelectedIdChanged(UUID?) case onAppTermination case deleteButtonTapped(UUID) case onDeleteCommand case onCloseTabCommand case onMove(from: IndexSet, to: Int) case onMoveUpCommand case onMoveDownCommand case deleteNow(UUID) case pprofs(IdentifiedActionOf) case drop(DropAndImportFeature.Action) } @Dependency(\.uuid) var uuid @Dependency(\.continuousClock) var clock var body: some ReducerOf { Scope(state: \.drop, action: \.drop) { DropAndImportFeature() } Reduce { state, action in switch action { case .onAppear: self.sync(&state) return .none case let .onPprofsSelectedIdChanged(uuidOpt): state.pprofsSelectedId = uuidOpt return .none case let .deleteButtonTapped(id): if let pprof = state.pprofs.first(where: { $0.id == id }) { state.basics.removeAll(where: { $0.id == pprof.basic.id }) switch pprof.period { case let .launching(la): if let process = la.process { if process.isRunning { process.terminate() } } case let .success(su): if su.process.isRunning { su.process.terminate() } default: let _ = 1 } return .run { send in await send(.pprofs(.element(id: pprof.id, action: .period(.launching(.stop))))) await send(.pprofs(.element(id: pprof.id, action: .period(.success(.stop))))) await send(.deleteNow(id)) } } log.warning("onDeleteTapped: pprof does not exist, id: \(id)") return .none case .onDeleteCommand: if let id = state.pprofsSelectedId { return .send(.deleteButtonTapped(id)) } return .none case let .deleteNow(id): delete(&state, id) return .none case .onAppTermination: terminateAllProcesses(state: &state) return .none case .onCloseTabCommand: state.pprofsSelectedId = nil return .none case let .onMove(from, to): move(&state, from, to) return .none case .onMoveUpCommand: if let pprofsSelectedId = state.pprofsSelectedId { moveUp(&state, pprofsSelectedId) } return .none case .onMoveDownCommand: if let pprofsSelectedId = state.pprofsSelectedId { moveDown(&state, pprofsSelectedId) } return .none case let .pprofs(.element(id: id, action: .delegate(.onPprofsSelectedIdChanged))): state.pprofsSelectedId = id return .none case .pprofs: return .none case let .drop(.delegate(.addNewBasic(basic))): self.addNewBasics(&state, [basic]) return .run { send in try await clock.sleep(for: .seconds(0.2)) await send(.onPprofsSelectedIdChanged(basic.id)) } case let .drop(.delegate(.addNewBasics(basics))): self.addNewBasics(&state, basics) if let first = basics.first { return .run { send in try await clock.sleep(for: .seconds(0.2)) await send(.onPprofsSelectedIdChanged(first.id)) } } return .none case let .drop(.delegate(.selectPProf(uuid))): if state.pprofs.contains(where: { $0.id == uuid }) { state.pprofsSelectedId = uuid } return .none case .drop: return .none } } .forEach(\.pprofs, action: \.pprofs) { DetailFeature() } } func addNewBasics(_ state: inout Self.State, _ basics: [PProfBasic]) { state.basics.insert(contentsOf: basics, at: 0) state.synced = false sync(&state) } func sync(_ state: inout Self.State) { if !state.synced { var result: IdentifiedArrayOf = .init() for basic in state.$basics.elements { if let pprof = state.pprofs.first(where: { $0.id == basic.id }) { result.append(pprof) } else { let pprof = DetailFeature.State(basic: basic, period: .idle(.init())) result.append(pprof) } } state.pprofs = result state.synced = true } } func terminateAllProcesses(state: inout Self.State) { for pprof in state.pprofs { if case let .launching(l) = pprof.period, let process = l.process, process.isRunning { let pid = process.processIdentifier log.info("terminating process [pid=\(pid)]") process.terminate() } else if case let .success(s) = pprof.period, s.process.isRunning { let pid = s.process.processIdentifier log.info("terminating process [pid=\(pid)]") s.process.terminate() } } } /// Remove an element and update the selection accordingly private func delete(_ state: inout Self.State, _ id: UUID) { // Remember the selection index before deleting an element var originalIndex: Int? if let selection = state.pprofsSelectedId { originalIndex = state.pprofs.firstIndex { $0.id == selection } } state.pprofs.removeAll { $0.id == id } if let selection = state.pprofsSelectedId { // If the selected element is deleted if !state.pprofs.contains(where: { $0.id == selection }), let originalIndex = originalIndex { if originalIndex < state.pprofs.count { // If there were elements after it, select the next one state.pprofsSelectedId = state.pprofs[originalIndex].id } else { // Otherwise(selection was the last element), select the last element in the list state.pprofsSelectedId = state.pprofs.last?.id } } } } private func move(_ state: inout Self.State, _ source: IndexSet, _ destination: Int) { state.pprofs.move(fromOffsets: source, toOffset: destination) } private func moveUp(_ state: inout Self.State, _ pprofsSelectedId: UUID) { guard let index = state.pprofs.firstIndex(where: { $0.id == pprofsSelectedId }) else { return } if index - 1 >= 0 { state.pprofs.move(fromOffsets: IndexSet([index]), toOffset: index - 1) } } private func moveDown(_ state: inout Self.State, _ pprofsSelectedId: UUID) { guard let index = state.pprofs.firstIndex(where: { $0.id == pprofsSelectedId }) else { return } if index + 2 <= state.pprofs.count { state.pprofs.move(fromOffsets: IndexSet([index]), toOffset: index + 2) } } } extension PersistenceReaderKey where Self == PersistenceKeyDefault>> { static var approf: Self { PersistenceKeyDefault( .fileStorage(.homeDirectory.appending(component: ".approf.json")), [] ) } } ================================================ FILE: approf/View/DetailFeature.swift ================================================ import ComposableArchitecture import Foundation import WebKit @Reducer struct DetailFeature { @ObservableState struct State: Equatable, Identifiable { var id: UUID @Shared var basic: PProfBasic var period: PProfPeriod.State var subViewType: DetailSubViewType var uth: UnderTheHood.State var showGoToWEB = false init(basic: Shared, period: PProfPeriod.State) { self.id = basic.id _basic = basic self.period = period self.subViewType = .underTheHood self.uth = UnderTheHood.State(basic: basic) } } enum Action { case period(PProfPeriod.Action) case uth(UnderTheHood.Action) case onAppear case onSwitchViewChanged(DetailSubViewType) case onStartButtonTapped case launchButtonTapped case relaunchButtonTapped case stopButtonTapped case goToWEBButtonTapped case onLaunch case delegate(Delegate) @CasePathable enum Delegate: Equatable { case onPprofsSelectedIdChanged } } @Dependency(\.continuousClock) var clock var body: some ReducerOf { Scope(state: \.uth, action: \.uth) { UnderTheHood() } Reduce { state, action in switch action { case .delegate, .onAppear: return .none case .onStartButtonTapped: switch state.period { case .idle, .terminated: state.period = .launching(LaunchingFeature.State(basic: state.$basic, goToWebOnSuccess: true)) return .merge( .send(.period(.launching(.start))), .send(.delegate(.onPprofsSelectedIdChanged)) ) default: return .none } case let .onSwitchViewChanged(t): state.subViewType = t state.showGoToWEB = false return .none case .onLaunch, .launchButtonTapped, .period(.failure(.delegate(.launchButtonTapped))), .period(.idle(.delegate(.launchButtonTapped))), .period(.terminated(.delegate(.launchButtonTapped))): self.cleanUp(state: &state) state.period = .launching(LaunchingFeature.State(basic: state.$basic)) return .send(.period(.launching(.start))) case .relaunchButtonTapped: return .merge( .send(.period(.launching(.stop))), .send(.onLaunch) ) case let .period(.launching(.delegate(.onSuccess(process, portReady, goToWEB)))): let conf = WKWebViewConfiguration() conf.defaultWebpagePreferences.allowsContentJavaScript = true conf.allowsAirPlayForMediaPlayback = false conf.preferences.setValue(true, forKey: "developerExtrasEnabled") let wk = WKWebView(frame: .zero, configuration: conf) wk.layer?.borderWidth = 0 state.period = .success(.init(basic: state.$basic, process: process, port: portReady, wk: wk)) if goToWEB { state.subViewType = .graphic } else { state.showGoToWEB = true } return .merge( .run { _ in await wk.load(URLRequest(url: URL(string: "http://localhost:\(portReady)")!)) }, .run { send in try await self.clock.sleep(for: .seconds(0.3)) await send(.period(.success(.onFullyLoaded)), animation: .default) } ) case let .period(.launching(.delegate(.onFailed(cause)))): state.period = .failure(.init(basic: state.$basic, cause: cause)) return .none case .stopButtonTapped: return .merge( .send(.period(.launching(.stop))), .send(.period(.success(.stop))) ) case .goToWEBButtonTapped: state.subViewType = .graphic state.showGoToWEB = false return .none case .period(.launching(.delegate(.onTermimated))), .period(.success(.delegate(.onTermimated))): state.period = .terminated(.init()) return .none case .period: return .none case .uth: return .none } } .ifLet(\.period.idle, action: \.period.idle) { IdleFeature() } .ifLet(\.period.terminated, action: \.period.terminated) { IdleFeature() } .ifLet(\.period.launching, action: \.period.launching) { LaunchingFeature() } .ifLet(\.period.success, action: \.period.success) { SuccessFeature() } .ifLet(\.period.failure, action: \.period.failure) { FailureFeature() } } func cleanUp(state: inout Self.State) { state.subViewType = .underTheHood state.basic.terminalOutput = [] state.basic.httpDetectLog = [] state.basic.finalCommandArgs = [] } } extension DetailFeature { @Reducer(state: .equatable) enum PProfPeriod { case idle(IdleFeature) case terminated(IdleFeature) case launching(LaunchingFeature) case failure(FailureFeature) case success(SuccessFeature) func isRunning() -> Bool { if case .launching = self { return true } else { return false } } } } extension DetailFeature.State { var isRunning: Bool { if case .success = self.period { return true } else { return false } } } enum DetailSubViewType: String, Codable, CaseIterable { case underTheHood = "Terminal", graphic = "WEB" func switchView() -> Self { switch self { case .underTheHood: .graphic case .graphic: .underTheHood } } } ================================================ FILE: approf/View/DetailView.swift ================================================ import ComposableArchitecture import SwiftUI struct DetailView: View { @Bindable var store: StoreOf var body: some View { VStack { if store.subViewType == .graphic, let s = store.scope(state: \.period.success, action: \.period.success) { SuccessView(store: s) } else { UnderTheHoodView(store: store.scope(state: \.uth, action: \.uth), detailStore: store) } } .padding(.horizontal, 8) .padding(.bottom, 8) .onAppear { store.send(.onAppear) } .toolbar { if case .success = store.period { ToolbarItem(placement: .navigation) { Picker("", selection: $store.subViewType.sending(\.onSwitchViewChanged)) { ForEach(DetailSubViewType.allCases, id: \.self) { c in Text("\(c.rawValue)") } } .pickerStyle(.segmented) .labelsHidden() .background(RoundedRectangle(cornerRadius: 6).fill(.thinMaterial)) } } } .animation(.default, value: store.period) .animation(.easeInOut(duration: 0.1), value: store.subViewType) } } struct StatusView: View { var body: some View { HStack(spacing: 1) { Image(systemName: "smallcircle.filled.circle") .symbolRenderingMode(.palette) .foregroundStyle(.green, .clear) .scaleEffect(2) .symbolEffect(.pulse, options: .speed(0.5)) Text("Running") .foregroundStyle(.secondary) } .font(.caption) .padding(.horizontal, 3) .padding(.vertical, 1) .background(.ultraThinMaterial) .clipShape(RoundedRectangle(cornerRadius: 7)) .padding(.trailing, 4) .padding(.bottom, 2) } } ================================================ FILE: approf/View/DetectingHTTPView.swift ================================================ import SwiftUI import ComposableArchitecture struct DetectingHTTPView: View { @Bindable var store: StoreOf var body: some View { switch store.period { case .launching: TringHttpView() HttpResultView(basic: store.basic) case .failure: HttpResultView(basic: store.basic) default: EmptyView() } } } struct TringHttpView: View { var body: some View { HStack { // bug: .rotate effect is not woking on macos 15 beta 3 // if #available(macOS 15.0, *) { // Image(systemName: "circle.hexagonpath.fill") // .symbolEffect(.rotate) // } Spacer().frame(width: 5) ProgressView() .scaleEffect(0.5) .frame(width: 5, height: 5) Text("Trying HTTP request") .font(.caption) .foregroundColor(.secondary) } } } struct HttpResultView: View { let basic: PProfBasic var body: some View { if let last = basic.httpDetectLog.last { HStack(alignment: .firstTextBaseline) { Text("HTTP result: ").foregroundStyle(.secondary) switch last { case let .err(str): Image("circle.fill").foregroundStyle(.red) Text(str).lineLimit(5) case let .http(code, html): Text("\(code)").foregroundStyle(code <= 299 && code >= 200 ? .green : .red) Text(html).lineLimit(5) } } .animation(.linear, value: last) } } } ================================================ FILE: approf/View/DragNDrop/DropAndAppendFeature.swift ================================================ import ComposableArchitecture import Foundation @Reducer struct DropAndAppendFeature { @ObservableState struct State: Equatable { @Shared var basic: PProfBasic } enum Action { case onDropEnds([URL]) } var body: some ReducerOf { Reduce { state, action in switch action { case let .onDropEnds(urls): let u = urls.map { $0.path(percentEncoded: false) } state.basic.filePaths.append(contentsOf: u) return .none } } } } ================================================ FILE: approf/View/DragNDrop/DropAndAppendView.swift ================================================ import Combine import ComposableArchitecture import SwiftUI import UniformTypeIdentifiers struct DropAndAppendView: View { @Bindable var store: StoreOf @State var dropping = false var body: some View { Rectangle() .fill(.clear) .allowsHitTesting(false) .onDrop(of: allowedImportFileTypes, delegate: DropFileDelegate(dropping: $dropping) { urls in store.send(.onAddURLs(urls)) }) } } ================================================ FILE: approf/View/DragNDrop/DropAndImportFeature.swift ================================================ import ComposableArchitecture import Foundation @Reducer struct DropAndImportFeature { @Reducer(state: .equatable) enum Destination { case uth(UnderTheHood) } @ObservableState struct State: Equatable { @Presents var destination: Destination.State? } enum Action { case destination(PresentationAction) case onDropEnds([URL]) case delegate(Delegate) @CasePathable enum Delegate { case addNewBasic(PProfBasic) case addNewBasics([PProfBasic]) case selectPProf(UUID) } } @Dependency(\.continuousClock) var clock @Dependency(\.uuid) var uuid @Dependency(\.date) var date var body: some ReducerOf { Reduce { state, action in switch action { case .delegate: return .none case let .onDropEnds(urls): let filePaths = urls.map { $0.path(percentEncoded: false) } if filePaths.isEmpty { return .none } else if filePaths.count == 1 { let basic = PProfBasic(uuid: uuid(), filePaths: filePaths, createdAt: date.now, presentation: .dft) return .send(.delegate(.addNewBasic(basic))) } else if filePaths.count == 2 { state.destination = .uth(UnderTheHood.State(basic: Shared(PProfBasic(uuid: uuid(), filePaths: filePaths, createdAt: date.now, presentation: .diff)))) return .none } else { state.destination = .uth(UnderTheHood.State(basic: Shared(PProfBasic(uuid: uuid(), filePaths: filePaths, createdAt: date.now, presentation: .acc)))) return .none } case .destination(.presented(.uth(.delegate(.onConfirmImportButtonTapped)))): guard case let .uth(uthFeature) = state.destination else { return .none } state.destination = nil // When DropView disappears, the animation for the profs list doesn't work. // To fix this, update the profs list after DropView has fully disappeared. if case .dft = uthFeature.basic.presentation { let basics = uthFeature.basic.filePaths.reversed().map { PProfBasic(uuid: uuid(), filePaths: [$0], createdAt: date.now, presentation: .dft) } return .run { send in try await clock.sleep(for: .seconds(0.25)) await send(.delegate(.addNewBasics(basics))) } } else { let basic = PProfBasic(uuid: uuid(), filePaths: uthFeature.basic.filePaths, createdAt: date.now, presentation: uthFeature.basic.presentation) return .run { send in try await clock.sleep(for: .seconds(0.25)) await send(.delegate(.addNewBasic(basic))) } } case .destination: return .none } } .ifLet(\.$destination, action: \.destination) } } ================================================ FILE: approf/View/DragNDrop/DropAndImportView.swift ================================================ import Combine import ComposableArchitecture import SwiftUI import UniformTypeIdentifiers struct DropAndImportView: View { @Bindable var store: StoreOf @State var viewSize = CGSize(width: 800, height: 600) @State var dropping = false @State var uiState = UIState.shared var body: some View { Rectangle() .fill(.clear) .onGeometryChange(for: CGSize.self) { proxy in proxy.size } action: { viewSize = $0 } .allowsHitTesting(false) .overlay { if dropping && store.destination == nil { GradientBackgroundAnimation() } } .animation(.default, value: dropping) .onDrop(of: allowedImportFileTypes, delegate: DropFileDelegate(dropping: $dropping) { urls in if store.destination == nil{ store.send(.onDropEnds(urls)) } }) .sheet( item: $store.scope(state: \.destination?.uth, action: \.destination.uth) ) { importingStore in ImportView(store: importingStore) .padding(20) .frame(width: viewSize.width * 0.7, height: viewSize.height * 0.8) .presentationCornerRadius(20) .presentationBackgroundInteraction(.disabled) } } } class DropFileDelegate: DropDelegate { @Binding var dropping: Bool let onDropEnds: ([URL]) -> Void let excludeArea: CGRect? private var cancellables = Set() init(dropping: Binding, excludeArea: CGRect? = nil, onDropEnds: @escaping ([URL]) -> Void) { _dropping = dropping self.excludeArea = excludeArea self.onDropEnds = onDropEnds } func validateDrop(info: DropInfo) -> Bool { return true } func dropEntered(info: DropInfo) { if let excludeArea = excludeArea, excludeArea.contains(info.location) { dropping = false } else { dropping = true } } func dropUpdated(info: DropInfo) -> DropProposal? { dropEntered(info: info) return nil } func dropExited(info: DropInfo) { dropping = false } func performDrop(info: DropInfo) -> Bool { let providers = info.itemProviders(for: allowedImportFileTypes) let futureArray = providers.compactMap { provider in allowedImportFileTypes.compactMap { ptype in if provider.hasItemConformingToTypeIdentifier(ptype.identifier) { return Future { promise in _ = provider.loadFileRepresentation(for: ptype, openInPlace: true) { url, _, error in if let error = error { print("Error loading file representation: \(error)") promise(.success(nil)) return } promise(.success(url)) } } } return nil } }.flatMap { $0 } if futureArray.isEmpty { return true } Publishers.MergeMany(futureArray) .collect() .receive(on: DispatchQueue.main) .sink { result in let urls = result.compactMap { $0 } as [URL] log.info("imported \(urls.count) files") Task { @MainActor in self.onDropEnds(urls) } } .store(in: &cancellables) return true } } ================================================ FILE: approf/View/DragNDrop/ImportView.swift ================================================ import ComposableArchitecture import SwiftUI struct ImportView: View { @Bindable var store: StoreOf var body: some View { VStack(spacing: 20) { FileListView(store: store, importing: true) Spacer() CommandPreviewView(store: store, importing: true) } .background { ShortcutsView(store: store) } .toolbar { toolbar() } } @ToolbarContentBuilder private func toolbar() -> some ToolbarContent { ToolbarItem(placement: .destructiveAction) { Button("Cancel(ESC)", role: .cancel) { store.send(.onCancelImportButtonTapped) } } ToolbarItem(placement: .confirmationAction) { Button("Done⏎") { store.send(.delegate(.onConfirmImportButtonTapped)) } .keyboardShortcut(.return, modifiers: []) } } } struct ImportViewV2App: App { var body: some Scene { WindowGroup { ImportView(store: UnderTheHood.mock) } } } ================================================ FILE: approf/View/FailureFeature.swift ================================================ import ComposableArchitecture import Foundation extension FailureFeature { enum Cause: Equatable { case file(String) case process(String) case httpDetect(String) case pipeDetect(String) } } @Reducer struct FailureFeature { @ObservableState struct State: Equatable { @Shared var basic: PProfBasic let cause: Cause } enum Action { case launchButtonTapped case delegate(Delegate) @CasePathable enum Delegate { case launchButtonTapped } } var body: some ReducerOf { Reduce { state, action in switch action { case .delegate: .none case .launchButtonTapped: .send(.delegate(.launchButtonTapped)) } } } } ================================================ FILE: approf/View/IdleFeature.swift ================================================ import ComposableArchitecture import Foundation @Reducer struct IdleFeature { @ObservableState struct State: Equatable {} enum Action { case delegate(Delegate) @CasePathable enum Delegate: Equatable { case launchButtonTapped } } var body: some ReducerOf { Reduce { _, action in switch action { case .delegate: .none } } } } ================================================ FILE: approf/View/LaunchingFeature.swift ================================================ import ComposableArchitecture import Foundation @Reducer struct LaunchingFeature { @ObservableState struct State: Equatable { @Shared var basic: PProfBasic var process: Process? var readingTerminal = false var portReady: UInt16? var detectingHttp = false var httpReady = false var goToWebOnSuccess = false } enum Action { case start case stop case launchingTimeout(Int) case readTerminal(AsyncStream) case portReady(UInt16) case tryHttp(UInt16) case httpReady case fail(FailureFeature.Cause) case terminalUpdated(String) case httpUpdated(HTTPResult) case delegate(Delegate) @CasePathable enum Delegate: Equatable { case onSuccess(Process, UInt16, Bool) // goToWEB: Bool case onFailed(FailureFeature.Cause) case onTermimated } } enum CancelID { case terminalReader, httpDetector } @Dependency(\.continuousClock) var clock @Dependency(\.date) var date var body: some ReducerOf { Reduce { state, action in switch action { case .delegate: return .none case .start: if let p = state.process { log.error("process already exeists") return .send(.fail(.process("process already exeists. process.processIdentifier: \(p.processIdentifier)"))) } state.basic.terminalOutput = [] state.basic.httpDetectLog = [] var processOptional: Process? var streamOptional: AsyncStream? do { let args = state.basic.commandArgs() let (commandArgs, process, terminalStream) = try createPProfProcess(args) state.process = process state.basic.finalCommandArgs = commandArgs processOptional = process streamOptional = terminalStream } catch { return .send(.fail(.process(error.localizedDescription))) } let process = processOptional! let stream = streamOptional! return .run { send in do { try process.run() log.info("process.isRunning: \(process.isRunning), process.processIdentifier.: \(process.processIdentifier)") if !process.isRunning { log.error("process.terminationStatus: \(process.terminationStatus), process.terminationReason: \(process.terminationReason)") } } catch { return await send(.fail(.process(error.localizedDescription))) } await send(.readTerminal(stream)) await send(.launchingTimeout(5)) } case .stop: if let process = state.process { if process.isRunning { process.terminate() } state.process = nil } return .send(.delegate(.onTermimated)) case let .readTerminal(stream): state.readingTerminal.toggle() if state.readingTerminal { return .run { send in for try await data in stream { log.debug("read from stream: \(data)") await send(.terminalUpdated(data)) } log.debug("finished reading from stream") } .cancellable(id: CancelID.terminalReader) } else { return .cancel(id: CancelID.terminalReader) } case let .portReady(port): state.portReady = port return .send(.tryHttp(port)) case let .tryHttp(port): state.detectingHttp.toggle() if state.detectingHttp { let httpAddr = "http://localhost:\(port)" guard let httpUrl = URL(string: httpAddr) else { let e = "can't make a URL from \(httpAddr)" log.error("\(e)") return .none } return .run { send in let (ok, httpResult) = await detectHttpOk2(httpUrl: httpUrl, bodyShouldContain: "

pprof

") if ok { return await send(.httpReady) } else { await send(.httpUpdated(httpResult)) } for await _ in self.clock.timer(interval: .seconds(1)) { let (ok, httpResult) = await detectHttpOk2(httpUrl: httpUrl, bodyShouldContain: "

pprof

") if ok { return await send(.httpReady) } else { await send(.httpUpdated(httpResult)) } } } .cancellable(id: CancelID.httpDetector) } else { return .cancel(id: CancelID.httpDetector) } case let .terminalUpdated(text): state.basic.terminalOutput.append(.init(date: date.now, std: .out, text: text)) if let port = state.basic.terminalOutput.map({ $0.text }).joined().extractPort() { state.portReady = port return .send(.portReady(port)) } return .none case let .httpUpdated(HTTPResult): state.basic.httpDetectLog.append(HTTPResult) return .none case .httpReady: if let process = state.process, let port = state.portReady { return .send(.delegate(.onSuccess(process, port, state.goToWebOnSuccess))) } else { return .send(.fail(.process("failed: let process = state.process, let port = state.portReady"))) } case let .launchingTimeout(timeout): if timeout > 0 { return .run { send in try await self.clock.sleep(for: .seconds(timeout)) await send(.launchingTimeout(0)) } } if state.portReady == nil || !state.httpReady || state.process?.isRunning ?? false { return .send(.fail(.process("launch timeout"))) } return .none case let .fail(cause): state.process?.terminate() return .send(.delegate(.onFailed(cause))) } } } } extension LaunchingFeature.State {} enum HTTPResult: Equatable { case http(code: Int, html: String), err(String) } ================================================ FILE: approf/View/NavigaionView.swift ================================================ import ComposableArchitecture import SwiftUI struct NavigaionView: View { @Bindable var store: StoreOf var body: some View { NavigationSplitView { list().navigationSplitViewColumnWidth(min: 200, ideal: 300) } detail: { detail() } .navigationTitle("") .onAppear { store.send(.onAppear) } } @ViewBuilder private func list() -> some View { ScrollViewReader { proxy in List(selection: $store.pprofsSelectedId.sending(\.onPprofsSelectedIdChanged).animation()) { ForEach(store.scope(state: \.pprofs, action: \.pprofs), id: \.basic.id) { pprofStore in PProfRowView(store: pprofStore) .addHiddenView(pprofStore.id) { if store.pprofsSelectedId == pprofStore.id { rowContextMenu(pprofUUID: pprofStore.id) } } .contextMenu { rowContextMenu(pprofUUID: pprofStore.id) } .listRowSeparator(.visible) .listRowSeparatorTint(.secondary.opacity(0.5)) .id(pprofStore.id) } .onMove { from, to in store.send(.onMove(from: from, to: to), animation: .default) } } .onChange(of: store.pprofs.count) { old, neu in // scroll to top of the list when new elements are added if neu > old, let first = store.pprofs.first { withAnimation { proxy.scrollTo(first.id, anchor: .top) } } } } } @ViewBuilder private func detail() -> some View { if let selectedId = store.pprofsSelectedId, let st = store.scope(state: \.pprofs[id: selectedId], action: \.pprofs[id: selectedId]) { DetailView(store: st).id(st.id) .sc("w", modifiers: [.command]) { store.send(.onCloseTabCommand) } } else { WelcomeView() } } @ViewBuilder private func rowContextMenu(pprofUUID: UUID) -> some View { Button(action: { store.send(.onMoveUpCommand, animation: .default) }) { Text("Move Up") } .keyboardShortcut(.upArrow, modifiers: [.option, .command]) Button(action: { store.send(.onMoveDownCommand, animation: .default) }) { Text("Move Down") } .keyboardShortcut(.downArrow, modifiers: [.option, .command]) Button(action: { store.send(.deleteButtonTapped(pprofUUID)) }) { Text("Delete").foregroundStyle(.red) } .keyboardShortcut(.delete, modifiers: [.shift, .command]) } } ================================================ FILE: approf/View/PProfRowView.swift ================================================ import ComposableArchitecture import SwiftUI struct PProfRowView: View { @Bindable var store: StoreOf @State var hoveringOnButton = false @State var hoveringOnRow = false var body: some View { VStack(alignment: .leading, spacing: 4) { name() HStack(alignment: .firstTextBaseline) { meta() Spacer() Text(store.basic.createdAt.humanReadable()).help("This record is created at \(store.basic.createdAt).") buttons() } .font(.callout) .foregroundStyle(.secondary) .animation(.default, value: store.isRunning) } .onHover { h in withAnimation { hoveringOnRow = h } } .contentShape(RoundedRectangle(cornerRadius: 8)) } @ViewBuilder func buttons() -> some View { ZStack { if store.isRunning { if hoveringOnButton { stopButton() } else { runningButton() } } else if showStartButton { startButton() } } .onHover { h in withAnimation { hoveringOnButton = h } } } var showStartButton: Bool { switch store.period { case .idle, .terminated, .failure: return hoveringOnRow default: return false } } @ViewBuilder func startButton() -> some View { Button(action: { store.send(.onStartButtonTapped) }) { Image(systemName: "restart.circle") .foregroundStyle(.foreground) } .buttonStyle(.plain) .transition(.movingParts.iris(blurRadius: 50)) .help("Start pprof process and show the WEB page.") } @ViewBuilder func runningButton() -> some View { Image(systemName: "circle.fill") .foregroundStyle(.green) .scaleEffect(0.4) .transition(.movingParts.iris(blurRadius: 50)) .help("pprof process is running") } @ViewBuilder func stopButton() -> some View { Image(systemName: "square.fill") .foregroundStyle(.red.gradient) .scaleEffect(1.3) .transition(.movingParts.iris(blurRadius: 50)) .onTapGesture { store.send(.period(.success(.stop))) } .help("Terminate pprof process") } @ViewBuilder func name() -> some View { Text(store.basic.computedName.forceCharWrapping) .font(.title3) .fontWeight(.semibold) .lineLimit(2) } @ViewBuilder func meta() -> some View { let count = store.basic.filePaths.count if count > 1 { HStack(alignment: .firstTextBaseline, spacing: 0) { Image(systemName: "document") Text("×\(count)") } .help("This record contains \(count) files.") } if store.basic.presentation != .dft { Text("\(store.basic.presentation)").help(store.basic.presentation.explanation) } } } ================================================ FILE: approf/View/Setting/DefaultGzAppView.swift ================================================ // Created for approf in 2024 import AppKit import SwiftUI import UniformTypeIdentifiers struct DefaultGzAppView: View { @State private var defaultApp: GzApp = .dummy @State var apps: [GzApp] = [] var body: some View { GridRow { Text("Open `.pb.gz` with").gridColumnAlignment(.trailing) // Neither image.frame() nor VStack{Text() Text()} works in Picker Picker("", selection: $defaultApp) { ForEach(apps, id: \.self) { app in HStack(alignment: .center) { Image(nsImage: app.icon.resize(withSize: .init(width: 18, height: 18))) if app.hasDupName { Text(app.name) + Text("\n") + Text(app.url.nicePath).foregroundStyle(.secondary).font(.footnote) } else { Text(app.name) } } } } .pickerStyle(.automatic) .labelsHidden() } .onAppear { guard let url = Bundle.main.url(forResource: "test-default-app.pb", withExtension: "gz") else { log.error("test-default-app.pb.gz not found") return } self.loadApps(for: url) self.loadDefaultApp(for: url) } .onChange(of: defaultApp) { old, neu in if old != .dummy && neu != .dummy { log.info("setting \(neu.url) as default app for .gz") NSWorkspace.shared.setDefaultApplication(at: neu.url, toOpen: UTType.gzip) { err in if let err = err { log.error("NSWorkspace.shared.setDefaultApplication error: \(err)") } } } } } func loadApps(for url: URL) { let appURLs = NSWorkspace.shared.urlsForApplications(toOpen: url) var loadedApps: [GzApp] = [] var names: [String] = [] for appURL in appURLs { let appName = appURL.lastPathComponent let icon = NSWorkspace.shared.icon(forFile: appURL.path) loadedApps.append(GzApp(name: appName, url: appURL, icon: icon, hasDupName: false)) names.append(appName) } if names.count == Set(names).count { apps = loadedApps } else { // some apps hav the same name var newApps: [GzApp] = [] for app in loadedApps { let hasDupName = names.count(where: { $0 == app.name }) > 1 newApps.append(.init(name: app.name, url: app.url, icon: app.icon, hasDupName: hasDupName)) } apps = newApps } } func loadDefaultApp(for url: URL) { let url = NSWorkspace.shared.urlForApplication(toOpen: url) if let url = url { let appName = url.lastPathComponent let icon = NSWorkspace.shared.icon(forFile: url.path) defaultApp = GzApp(name: appName, url: url, icon: icon, hasDupName: false) } } struct GzApp: Identifiable, Hashable { var id: String { url.absoluteString } let name: String let url: URL let icon: NSImage let hasDupName: Bool func hash(into hasher: inout Hasher) { hasher.combine(url) } static func == (lhs: GzApp, rhs: GzApp) -> Bool { return lhs.url == rhs.url } static var dummy = GzApp(name: "", url: URL(fileURLWithPath: ""), icon: .init(), hasDupName: false) } } ================================================ FILE: approf/View/Setting/GraphvizGuideView.swift ================================================ // Created for approf in 2024 import SwiftUI struct GraphvizGuideView: View { @ObservedObject var asm = AppStorageManager.shared @State var dotFileExists: (dotPath: String, exist: Bool)? = nil @FocusState private var isTextFieldFocused: Bool var showTitle = true var callback: ((Bool) -> Void)? = nil var body: some View { Group { GridRow { Text(showTitle ? "Graphviz folder" : "").gridColumnAlignment(.trailing) TextField("", text: $asm.graphvizBinDir) .fontDesign(.monospaced) .frame(width: 250) .focused($isTextFieldFocused) Button("Open") { if let dir = selectFolder(asm.graphvizBinDir) { asm.graphvizBinDir = dir } } } GridRow { Text("").gridColumnAlignment(.trailing) HStack(spacing: 2) { if let (path, exist) = dotFileExists { Spacer().frame(width: 2) Text(path) .fontDesign(.monospaced) if exist { Text("exists") .foregroundStyle(.secondary) Image(systemName: "checkmark.circle.fill") .foregroundStyle(.green) } else { Text("doesn't exist") Image(systemName: "xmark.circle.fill") .foregroundStyle(.red) } } } .font(.footnote) .foregroundStyle(.secondary) .gridCellColumns(2) } } .onAppear { if !asm.graphvizBinDir.isEmpty { dotFileExists = dotExist(asm.graphvizBinDir) callback?(dotFileExists?.exist ?? false) } } .onAppear(delay: .seconds(0.01)) { // prevent auto focus isTextFieldFocused = false } .onChange(of: asm.graphvizBinDir) { _, b in if !asm.graphvizBinDir.isEmpty { dotFileExists = dotExist(b) callback?(dotFileExists?.exist ?? false) } } } } ================================================ FILE: approf/View/Setting/SettingView.swift ================================================ import SwiftUI import UniformTypeIdentifiers struct SettingsView: View { @StateObject var asm = AppStorageManager.shared @Environment(\.dismiss) var dismiss @State var dotFileExists: (dotPath: String, exist: Bool)? = nil var body: some View { Group { if #available(macOS 15.0, *) { TabView(selection: $asm.selectedSettingsTab) { Tab("General", systemImage: "medal.star.fill", value: .general) { general() } Tab("Appearance", systemImage: "paintpalette", value: .appearance) { appearance() } } } else { TabView(selection: $asm.selectedSettingsTab) { general() .tag(SettingsTab.general) .tabItem { Image(systemName: "medal.star.fill").symbolRenderingMode(.multicolor) Text("General") } appearance() .tag(SettingsTab.appearance) .tabItem { Image(systemName: "paintpalette") Text("Appearance") } } } } .padding(20) .frame(width: 600, height: 400) .sc(",", modifiers: [.command]) { dismiss() } } @ViewBuilder func general() -> some View { VStack { Grid(alignment: .leadingFirstTextBaseline) { GraphvizGuideView() DefaultGzAppView() } } } @ViewBuilder func appearance() -> some View { Grid(alignment: .leadingFirstTextBaseline) { GridRow { Label("Color Scheme", systemImage: "paintpalette") .symbolRenderingMode(.multicolor) .modifier(RippleEffect(at: .zero, trigger: asm.colorScheme)) .gridColumnAlignment(.trailing) Picker("", selection: $asm.colorScheme) { ForEach(AppColorScheme.allCases, id: \.self) { c in Text("\(c.rawValue)") } } .pickerStyle(.segmented) .labelsHidden() .containerRelativeFrame(.horizontal) { x, _ in x / 2 } } if #available(macOS 15.0, *) { // .containerBackground(asm.materialType.actualMaterial, for: .window) is not available on 14 GridRow { Label("Background", systemImage: "rectangle") .modifier(RippleEffect(at: .zero, trigger: asm.materialType)) .gridColumnAlignment(.trailing) Picker("", selection: $asm.materialType) { ForEach(MaterialType.allCases, id: \.self) { c in Text("\(c.rawValue)") } } .pickerStyle(.automatic) .labelsHidden() .containerRelativeFrame(.horizontal) { x, _ in x / 4 } } } } } } enum SettingsTab: Int { case general case appearance case shortcuts } #Preview { SettingsView() } ================================================ FILE: approf/View/ShortcutView.swift ================================================ import KeyboardShortcuts import SwiftUI extension KeyboardShortcuts.Name { static let sidebar = Self("Show/Hide Sidebar", default: .init(.l, modifiers: [.command, .shift])) static let refresh = Self("Refresh the Page", default: .init(.r, modifiers: [.command])) static let openAFile = Self("Open a File", default: .init(.t, modifiers: [.command])) static let closeTab = Self("Close Tab", default: .init(.w, modifiers: [.command])) static let stop = Self("Stop", default: .init(.c, modifiers: [.control])) } struct ShortcutView: View { var body: some View { Form { KeyboardShortcuts.Recorder("Show/Hide Sidebar", name: .sidebar) KeyboardShortcuts.Recorder("Refresh the Page", name: .refresh) KeyboardShortcuts.Recorder("Open a File", name: .openAFile) KeyboardShortcuts.Recorder("Close Tab", name: .closeTab) KeyboardShortcuts.Recorder("Stop", name: .stop) } } } #Preview { ShortcutView() } ================================================ FILE: approf/View/SuccessFeature.swift ================================================ import ComposableArchitecture import Foundation import WebKit @Reducer struct SuccessFeature { @ObservableState struct State: Equatable { @Shared var basic: PProfBasic let process: Process let port: UInt16 let wk: WKWebView var fullyLoaded = false // this is a copy of basic, and is used detect if changes have been made before process is terminated let snapshot: PProfBasic init(basic: Shared, process: Process, port: UInt16, wk: WKWebView) { _basic = basic self.process = process self.port = port self.wk = wk self.snapshot = basic.wrappedValue } } enum Action { case stop case onFullyLoaded case onRefreshButtonTapped case onHomeButtonTapped case delegate(Delegate) @CasePathable enum Delegate: Equatable { case onTermimated } } var body: some ReducerOf { Reduce { state, action in switch action { case .delegate: return .none case .onFullyLoaded: state.fullyLoaded = true return .none case .onHomeButtonTapped: state.wk.load(URLRequest(url: URL(string: "http://localhost:\(state.port)")!)) return .none case .onRefreshButtonTapped: state.wk.reload() return .none case .stop: if state.process.isRunning { state.process.terminate() } return .send(.delegate(.onTermimated)) } } } } ================================================ FILE: approf/View/SuccessView.swift ================================================ import ComposableArchitecture import KeyboardShortcuts import SwiftUI struct SuccessView: View { @Bindable var store: StoreOf @State var invert = false @ObservedObject var asm = AppStorageManager.shared @Environment(\.openURL) var openURL @State var addrBarWidth: CGFloat = 300 @State var wkState = WKState() var body: some View { VStack { WkWrapper(wk: store.wk, wkState: wkState) .if(!asm.lightsOn) { $0.colorInvert() } } // Before 'wk' is fully loaded, it shows a background color. colorInvert turns this background color into a blinding foreground color. // Adding a short delay to prevent a color flash. This only occurs the first time 'wk' displays. .opacity(store.fullyLoaded ? 1 : 0) .toolbar { ToolbarItem(placement: .status) { LightsOnButton(lightsOn: $asm.lightsOn) .help("Toggle Lights\nJust a simple color inversion in the app window, no CSS involved.") .opacity(store.fullyLoaded ? 1 : 0) } ToolbarItem(placement: .status) { Button(action: { store.send(.onHomeButtonTapped) }) { Image(systemName: "house") } .buttonStyle(.borderless) .help("Home") .opacity(store.fullyLoaded ? 1 : 0) } ToolbarItem(placement: .status) { statusBar() .frame(width: addrBarWidth) .opacity(store.fullyLoaded ? 1 : 0) } ToolbarItem(placement: .automatic) { Button(action: { store.send(.stop) }) { Image(systemName: "square") } .padding(.horizontal, 10) .padding(.vertical, 4) .help("Terminate pprof process") .opacity(store.fullyLoaded ? 1 : 0) } } .onGeometryChange(for: CGFloat.self) { proxy in proxy.size.width / 2 } action: { self.addrBarWidth = $0 } } @State var wkRefreshCount = 0 @ViewBuilder func statusBar() -> some View { HStack(alignment: .firstTextBaseline) { Button(action: { if let url = wkState.url { openURL(url) } }) { Image(systemName: "safari") } .buttonStyle(.borderless) .help("Open in Browser") Spacer() if let url = wkState.url { Text(url.absoluteString) .lineLimit(1) .foregroundStyle(.foreground.opacity(0.8)) .modifier(RippleEffect(at: .zero, trigger: wkRefreshCount)) ProgressView() .scaleEffect(0.4) .frame(width: 5, height: 5) .opacity(wkState.loading ? 1 : 0) } Spacer() Button(action: { wkRefreshCount += 1 store.send(.onRefreshButtonTapped) }) { Image(systemName: "arrow.clockwise") } .buttonStyle(.borderless) .keyboardShortcut("r", modifiers: [.command]) .help("Refresh") } .padding(.horizontal, 6) .padding(.vertical, 4) .background( RoundedRectangle(cornerRadius: 10) .strokeBorder(.secondary.opacity(0.5), lineWidth: 0.5) ) } } ================================================ FILE: approf/View/UnderTheHood/ActionButtonView.swift ================================================ // Created for approf in 2024 import ComposableArchitecture import SwiftUI struct ActionButtonView: View { @Bindable var store: StoreOf var body: some View { HStack { VStack(alignment: .leading) { HStack { UTHStatusView(store: store) actionButton() } DetectingHTTPView(store: store) } Spacer() } } @ViewBuilder private func actionButton() -> some View { switch store.period { case .idle, .terminated: Button("Launch") { store.send(.launchButtonTapped) } .buttonStyle(BorderedProminentButtonStyle()) case .failure: Button("Relaunch") { store.send(.launchButtonTapped) } .buttonStyle(BorderedProminentButtonStyle()) case let .success(su): HStack(alignment: .firstTextBaseline) { Button("Stop") { store.send(.stopButtonTapped) } if !store.basic.equalsSnapshot(su.snapshot) { HStack(alignment: .firstTextBaseline, spacing: 0) { Text("Changes detected, ").foregroundStyle(.secondary) Button("reluanch") { store.send(.relaunchButtonTapped) } .buttonStyle(.plain) .foregroundStyle(.tint) Text("?").foregroundStyle(.secondary) } } else if store.showGoToWEB { HStack(alignment: .firstTextBaseline, spacing: 0) { Text("Go to ").foregroundStyle(.secondary) Button("WEB") { store.send(.goToWEBButtonTapped) } .buttonStyle(.plain) .foregroundStyle(.tint) Text("?").foregroundStyle(.secondary) } } } case .launching: Button("Cancel") { store.send(.stopButtonTapped) } } } } ================================================ FILE: approf/View/UnderTheHood/CommandPreviewView.swift ================================================ // Created for approf in 2024 import ComposableArchitecture import SwiftUI struct CommandPreviewView: View { @Bindable var store: StoreOf var importing: Bool = false var body: some View { VStack(alignment: .leading) { HStack(alignment: .bottom, spacing: 4) { Image(systemName: "command") Text("Command Preview") .foregroundStyle(.secondary) CopyButton { copiable } Spacer() if store.basic.filePaths.count > 1 { Picker("", selection: $store.basic.presentation.sending(\.onPresentationChanged)) { ForEach(PProfPresentation.allCases, id: \.self) { c in Text("\(c.rawValue)") .help(c.explanation) } } .pickerStyle(.segmented) .labelsHidden() .frame(width: 200) } } ScrollableTextBox(heightLimit: 100) { if importing && store.basic.presentation == .dft { ForEach(store.basic.filePaths, id: \.self) { fp in Text(CommandLine.commandPreview(.dft, [fp]).asPrintable()) } } else { ForEach(printable, id: \.self) { line in Text(line) } } } } } var copiable: String { if importing && store.basic.presentation == .dft { store.basic.filePaths.map { CommandLine.commandPreview(.dft, [$0]).asCopiable() } .joined(separator: "\n") } else { store.basic.commandPreview().asCopiable() } } var printable: [String] { if importing && store.basic.presentation == .dft { store.basic.filePaths.map { CommandLine.commandPreview(.dft, [$0]).asPrintable() } } else { store.basic.commandPreview().asPrintable().components(separatedBy: "\n") } } } ================================================ FILE: approf/View/UnderTheHood/FileListView.swift ================================================ // Created for approf in 2024 import ComposableArchitecture import Foundation import SwiftUI struct FileListView: View { @Bindable var store: StoreOf var importing: Bool = false @State var uiState = UIState.shared var body: some View { VStack(alignment: .leading) { HStack(alignment: .firstTextBaseline, spacing: 4) { Image(systemName: "folder") Text("Files") .foregroundStyle(.secondary) } VStack(spacing: 0) { List(selection: $store.selection.sending(\.onSelectionChanged)) { ForEach(store.basic.filePaths, id: \.self) { filePath in FileRowView( filePath: filePath, ignored: !importing && (store.basic.presentation == .dft && filePath != store.basic.filePaths.first), isBase: store.basic.presentation == .diff && filePath == store.basic.filePaths.first ) .addHiddenView(filePath) { if store.selection == filePath { rowContextMenu(filePath: filePath, deleteDisabled: store.basic.filePaths.count == 1) } } .contextMenu { rowContextMenu(filePath: filePath, deleteDisabled: store.basic.filePaths.count == 1) } } .onMove { from, to in store.send(.onMove(from: from, to: to), animation: .default) } } .scrollContentBackground(.hidden) Divider() HStack(alignment: .center, spacing: 2) { Button(action: { let urls = selectMultiFiles(utTypes: allowedImportFileTypes) if !urls.isEmpty { let filePaths = urls.map { $0.path(percentEncoded: false) } store.send(.onSelectFilesEnd(filePaths)) } }) { Image(systemName: "rectangle.fill") .opacity(0.001) .overlay { Image(systemName: "plus") } } .buttonStyle(.plain) .contentShape(Rectangle()) Divider().frame(height: 14) Button(action: { store.send(.onDeleteSelectedCommand) }) { Image(systemName: "rectangle.fill") .opacity(0.001) .overlay { Image(systemName: "minus") } } .buttonStyle(.plain) .contentShape(Rectangle()) Spacer() } .padding(.leading, 8) .padding(.vertical, 3) } .background( RoundedRectangle(cornerRadius: 10) .fill(.bar) .strokeBorder(.secondary.opacity(0.5), lineWidth: 0.5) ) } .overlay{ DropAndAppendView(store: store) } } @ViewBuilder private func rowContextMenu(filePath: String, deleteDisabled: Bool) -> some View { Button("Show in Finder") { showInFinder(filePath) } .keyboardShortcut("j", modifiers: [.command, .shift]) Button(action: { store.send(.onMoveUpCommand, animation: .default) }) { Text("Move Up") } .keyboardShortcut(.upArrow, modifiers: [.command]) Button(action: { store.send(.onMoveDownCommand, animation: .default) }) { Text("Move Down") } .keyboardShortcut(.downArrow, modifiers: [.command]) Button(action: { store.send(.onDeleteMenuTapped(filePath), animation: .default) }) { Text("Delete").foregroundStyle(.red) } .keyboardShortcut(.delete, modifiers: [.command]) .disabled(deleteDisabled) } } ================================================ FILE: approf/View/UnderTheHood/FileRowView.swift ================================================ import SwiftUI struct FileRowView: View { @State var failedToReadFileReason: String? @State var fileAttrs: FileAttrs? @State var hoveringOnLM = false let filePath: String let ignored: Bool let isBase: Bool var body: some View { VStack(alignment: .leading, spacing: 2) { Text(filePath.forceCharWrapping) .multilineTextAlignment(.leading) HStack(alignment: .firstTextBaseline) { Button(action: { showInFinder(filePath) }) { Image(systemName: "arrowshape.right.circle.fill") } .buttonStyle(PlainButtonStyle()) .help("Show in Finder") if let fileSize = fileAttrs?.fileSize { Text(fileSize.humanReadableFileSize()) .foregroundStyle(.secondary) } else { // reserve vertical space before filesize shows up fix unsatalbe row height Text("f").opacity(0) } if let reason = failedToReadFileReason { Text("error") .foregroundStyle(.white) .padding(.horizontal, 6) .background(Rectangle().fill(.red)) .clipShape(RoundedRectangle(cornerRadius: 8)) .tooltip(delay: 0.3) { Text(reason) .padding(.horizontal, 10) .padding(.vertical, 4) } } Spacer() if isBase { Text("base") .foregroundStyle(.background) .padding(.horizontal, 6) .background(Rectangle().fill(.teal)) .clipShape(RoundedRectangle(cornerRadius: 8)) } else if ignored { Text("ignored") .foregroundStyle(.background) .padding(.horizontal, 6) .background(Rectangle().fill(.orange)) .clipShape(RoundedRectangle(cornerRadius: 8)) } if let lastModified = fileAttrs?.lastModified { HStack(alignment: .firstTextBaseline, spacing: 2) { if hoveringOnLM { Text("Last Modified: ") } Text(lastModified.humanReadable()) } .foregroundStyle(.secondary) .onHover { h in withAnimation { hoveringOnLM = h } } } else { Text("f").opacity(0) } } .font(.footnote) } .task(priority: .background){ readFile() } } func readFile() { if fileAttrs != nil || failedToReadFileReason != nil { return } do { let attrs = try FileManager.default.attributesOfItem(atPath: filePath) if let fileSize = attrs[.size] as? UInt64, let date = attrs[.modificationDate] as? Date { Task { @MainActor in withAnimation { fileAttrs = .init(fileSize: fileSize, lastModified: date) } } } } catch { Task { @MainActor in withAnimation { failedToReadFileReason = error.localizedDescription } } } } struct FileAttrs { let fileSize: UInt64 let lastModified: Date } } ================================================ FILE: approf/View/UnderTheHood/ImportView.swift ================================================ import ComposableArchitecture import SwiftUI struct ImportView: View { @Bindable var store: StoreOf var body: some View { VStack(spacing: 20) { FileListView(store: store) CommandPreviewView(store: store) Spacer() } .background { ShortcutsView(store: store) } .toolbar { toolbar() } .onDisappear { store.send(.delegate(.onImportViewAutoDismissed)) } } @ToolbarContentBuilder private func toolbar() -> some ToolbarContent { ToolbarItem(placement: .destructiveAction) { Button("Cancel(ESC)", role: .cancel) { store.send(.delegate(.onCancelImportButtonTapped)) } } ToolbarItem(placement: .confirmationAction) { Button("Done⏎") { store.send(.delegate(.onConfirmImportButtonTapped)) } .keyboardShortcut(.return, modifiers: []) } } } struct ImportViewV2App: App { var body: some Scene { WindowGroup { ImportViewV2(store: UnderTheHood.mock) } } } ================================================ FILE: approf/View/UnderTheHood/NameFeature.swift ================================================ import ComposableArchitecture import Foundation @Reducer struct NameFeature { @ObservableState struct State: Equatable { @Shared var basic: PProfBasic var editMode: Bool = false } enum Action { case onNameChanged(String) } var body: some ReducerOf { Reduce { state, action in switch action { case let .onNameChanged(name): state.basic.name = name return .none } } } } ================================================ FILE: approf/View/UnderTheHood/NameView.swift ================================================ // Created for approf in 2024 import ComposableArchitecture import SwiftUI struct NameView: View { @Bindable var store: StoreOf var importing: Bool = false var body: some View { HStack(alignment: .firstTextBaseline, spacing: 4) { Text("Name") .foregroundStyle(.secondary) TextField(store.basic.computedName, text: $store.basic.name.sending(\.onNameChanged)) .textFieldStyle(.plain) .background(.clear) .padding(.leading, 8) .padding(.vertical, 3) .background( RoundedRectangle(cornerRadius: 6) .fill(.bar) .strokeBorder(.secondary.opacity(0.5), lineWidth: 0.5) ) } } } ================================================ FILE: approf/View/UnderTheHood/NotificationFeature.swift ================================================ import ComposableArchitecture import Foundation @Reducer struct NotificationFeature { @ObservableState struct State: Equatable { var text: String var seconds: UInt? } enum Action { case onAppear case countDown } enum CancelID { case timer } @Dependency(\.continuousClock) var clock @Dependency(\.dismiss) var dismiss var body: some ReducerOf { Reduce { state, action in switch action { case .onAppear: if let s = state.seconds { if s == 0 { fatalError("don't do this") } return .run { send in for await _ in self.clock.timer(interval: .seconds(1)) { await send(.countDown) } }.cancellable(id: CancelID.timer) } else { return .none } case .countDown: state.seconds? -= 1 if state.seconds == 0 { return .merge( .cancel(id: CancelID.timer), .run { _ in await dismiss() } ) } return .none } } } } ================================================ FILE: approf/View/UnderTheHood/ShortcutsView.swift ================================================ // Created for approf in 2024 import ComposableArchitecture import SwiftUI struct ShortcutsView: View { @Bindable var store: StoreOf var body: some View { Rectangle() .fill(.clear) .frame(width: 1, height: 1) .allowsHitTesting(false) .sc(.rightArrow, modifiers: []) { store.send(.onNextPresentationCommand, animation: .default) } .sc(.tab, modifiers: []) { store.send(.onNextPresentationCommand, animation: .default) } .sc(.leftArrow, modifiers: []) { store.send(.onPrevPresentationCommand, animation: .default) } .sc(.tab, modifiers: [.control]) { store.send(.onPrevPresentationCommand, animation: .default) } .sc(.tab, modifiers: [.shift]) { store.send(.onPrevPresentationCommand, animation: .default) } } } ================================================ FILE: approf/View/UnderTheHood/StatusView.swift ================================================ import SwiftUI import ComposableArchitecture struct UTHStatusView: View { @Bindable var store: StoreOf var body: some View { HStack(alignment: .firstTextBaseline) { switch store.period { case .idle: EmptyView() case .terminated: Image(systemName: "moon.zzz") Text("Terminated") case .launching: RotatingSF("arrow.clockwise") .foregroundStyle(.orange) Text("Launching") .foregroundStyle(.orange) EmptyView() case .failure: HeartSlash() Text("Failed") .foregroundStyle(.red) case .success: Image(systemName: "smallcircle.filled.circle") .foregroundStyle(.green) .symbolEffect(.pulse.wholeSymbol, options: .speed(0.5)) Text("Running") .foregroundStyle(.green) } } } } ================================================ FILE: approf/View/UnderTheHood/TerminalView.swift ================================================ // Created for approf in 2024 import ComposableArchitecture import SwiftUI struct TerminalView: View { @Bindable var store: StoreOf var body: some View { VStack(alignment: .leading) { HStack { Image(systemName: "apple.terminal") // .foregroundColor(.blue) Text("Terminal").foregroundStyle(.secondary) } if #available(macOS 15.0, *) { ScrollableTextBox(heightLimit: 100) { Text(store.basic.finalCommandArgs.asCopiable()) Text(" ") Text(store.basic.terminalOutput.map { $0.text }.joined()) } .defaultScrollAnchor(.bottom) } else { ScrollableTextBox(heightLimit: 100) { Text(store.basic.finalCommandArgs.asCopiable()) Text(" ") Text(store.basic.terminalOutput.map { $0.text }.joined()) } } } } } ================================================ FILE: approf/View/UnderTheHood/UTH.swift ================================================ import ComposableArchitecture import Foundation @Reducer struct UnderTheHood { @Reducer(state: .equatable) enum Destination { case notification(NotificationFeature) } @ObservableState struct State: Equatable { @Shared var basic: PProfBasic var selection: String? @Presents var destination: Destination.State? } enum Action { case destination(PresentationAction) case onSelectionChanged(String?) case onNameChanged(String) case onPresentationChanged(PProfPresentation) case onSelectFilesEnd([String]) case onAddURLs([URL]) case onMove(from: IndexSet, to: Int) case onDeleteSelectedCommand case onDeleteMenuTapped(String) case onMoveUpCommand case onMoveDownCommand case onNextPresentationCommand case onPrevPresentationCommand case newNotification(String) case newCountDownNotification(String, UInt) case onCancelImportButtonTapped case delegate(Delegate) @CasePathable enum Delegate { // MARK: for import view case onConfirmImportButtonTapped } } @Dependency(\.dismiss) var dismiss var body: some ReducerOf { Reduce { state, action in switch action { case .delegate, .destination: return .none case let .onPresentationChanged(p): changePresentation(&state, p) return .none case let .onSelectionChanged(filePath): state.selection = filePath return .none case let .onNameChanged(name): state.basic.name = name return .none case let .onSelectFilesEnd(filePaths): addFiles(&state, filePaths: filePaths) return .none case let .onAddURLs(urls): let u = urls.map { $0.path(percentEncoded: false) } addFiles(&state, filePaths: u) return .none case let .onMove(from, to): move(&state, from, to) return .none case .onDeleteSelectedCommand: if let selection = state.selection { delete(&state, selection) } return .none case let .onDeleteMenuTapped(filePath): delete(&state, filePath) return .none case .onMoveUpCommand: if let selection = state.selection { moveUp(&state, selection) } return .none case .onMoveDownCommand: if let selection = state.selection { moveDown(&state, selection) } return .none case .onNextPresentationCommand: changePresentation(&state, state.basic.presentation.next) return .none case .onPrevPresentationCommand: state.basic.presentation = state.basic.presentation.prev return .none case let .newNotification(text): state.destination = .notification(.init(text: text)) return .none case let .newCountDownNotification(text, seconds): state.destination = .notification(.init(text: text, seconds: seconds)) return .none case .onCancelImportButtonTapped: return .run { _ in await dismiss() } } } .ifLet(\.$destination, action: \.destination) } private func changePresentation(_ state: inout Self.State, _ presentation: PProfPresentation) { if state.basic.filePaths.count <= 1 { state.basic.presentation = .dft } else { state.basic.presentation = presentation } } private func move(_ state: inout Self.State, _ source: IndexSet, _ destination: Int) { state.basic.filePaths.move(fromOffsets: source, toOffset: destination) } private func addFiles(_ state: inout Self.State, filePaths: [String]) { var dup = 0 for fp in filePaths { if state.basic.filePaths.contains(fp) { dup += 1 } else { state.basic.filePaths.append(fp) } } if dup > 0 { return state.destination = .notification(.init(text: "\(dup) duplicate files ignored", seconds: 5)) } } /// Remove an element and update the selection accordingly private func delete(_ state: inout Self.State, _ filePath: String) { // Remember the selection index before deleting an element var originalIndex: Int? if let selection = state.selection { originalIndex = state.basic.filePaths.firstIndex(of: selection) } if state.basic.filePaths.count > 1 { state.basic.filePaths.removeAll { $0 == filePath } if state.basic.filePaths.count == 1 { state.basic.presentation = .dft } } if let selection = state.selection { // If the selected element is deleted if !state.basic.filePaths.contains(where: { $0 == selection }), let originalIndex = originalIndex { if originalIndex < state.basic.filePaths.count { // If there were elements after it, select the next one state.selection = state.basic.filePaths[originalIndex] } else { // Otherwise(selection was the last element), select the last element in the list state.selection = state.basic.filePaths.last } } } } private func moveUp(_ state: inout Self.State, _ filePath: String) { guard let index = state.basic.filePaths.firstIndex(where: { $0 == filePath }) else { return } if index - 1 >= 0 { state.basic.filePaths.move(fromOffsets: IndexSet([index]), toOffset: index - 1) } } private func moveDown(_ state: inout Self.State, _ filePath: String) { guard let index = state.basic.filePaths.firstIndex(where: { $0 == filePath }) else { return } if index + 2 <= state.basic.filePaths.count { state.basic.filePaths.move(fromOffsets: IndexSet([index]), toOffset: index + 2) } } } ================================================ FILE: approf/View/UnderTheHood/UnderTheHoodView.swift ================================================ import ComposableArchitecture import PopupView import SwiftUI struct UnderTheHoodView: View { @Bindable var store: StoreOf @Bindable var detailStore: StoreOf var body: some View { VStack(spacing: 20) { NameView(store: store) FileListView(store: store, importing: false) CommandPreviewView(store: store, importing: false) Spacer().frame(height: 10) TerminalView(store: store) ActionButtonView(store: detailStore) Spacer() } .background { ShortcutsView(store: store) } .popup(item: $store.scope(state: \.destination?.notification, action: \.destination.notification), itemView: { notiStore in NotificationView(store: notiStore) }) { $0.type(.floater()) .position(.bottomTrailing) .animation(.spring()) } } } ================================================ FILE: approf/View/WebIndicatorView.swift ================================================ import ComposableArchitecture import SwiftUI struct WebIndicatorView: View { let url: URL var wkRefreshCount: Int @State var infoPopOver = false var body: some View { HStack(spacing: 4) { Text(url.lastPathComponent) HStack { Button(action: { infoPopOver.toggle() }) { Image(systemName: "info.circle") } .buttonStyle(PlainButtonStyle()) .popover(isPresented: $infoPopOver) { HStack(spacing: 4) { let abs = url.path(percentEncoded: false) Text(abs) .padding(.horizontal, 10) .padding(.vertical, 4) CopyButton{ abs } } .padding(.horizontal, 10) .padding(.vertical, 4) } } } .modifier(RippleEffect(at: .zero, trigger: wkRefreshCount)) } } ================================================ FILE: approf/View/WelcomeView.swift ================================================ import SwiftUI struct WelcomeView: View { @ObservedObject private var asm = AppStorageManager.shared @State private var showGraphvizGuide = false @State private var titleHeight: CGFloat = 30 var body: some View { Group { if showGraphvizGuide { VStack { Spacer() HStack(alignment: .firstTextBaseline) { Image("Gopple").resizable().scaledToFit().frame(height: titleHeight * 2) Text(", have you got Graphviz installed?").font(.largeTitle) .onGeometryChange(for: CGFloat.self) { proxy in proxy.size.height } action: { titleHeight = $0 } } Spacer().frame(height: 20) Text("Let’s locate the bin folder of Graphviz; it should have the `dot` executable.") .font(.title2) .fontWeight(.thin) .foregroundStyle(.secondary) Spacer() Grid(alignment: .leadingFirstTextBaseline) { GraphvizGuideView(showTitle: false) { exist in if exist { Task.detached { try await Task.sleep(for: .seconds(1.5)) Task { @MainActor in withAnimation(.easeInOut(duration: 1.5)) { showGraphvizGuide = false } } } } else { showGraphvizGuide = true } } } Spacer() Spacer() HStack(alignment: .firstTextBaseline) { Text("brew install graphviz") .fontDesign(.monospaced) .textSelection(.enabled) CopyButton { "brew install graphviz" } } .padding(30) } } else { OpenFileHint() } } .onAppear { showGraphvizGuide = !dotExist(asm.graphvizBinDir).1 } } } struct OpenFileHint: View { var body: some View { VStack(alignment: .leading) { Label(title: { Text("Open a file from Finder") }) { Image(systemName: "cursorarrow.click.2") .foregroundStyle(.blue) } Label(title: { Text("or") }) { Image(systemName: "cursorarrow.click.2") .foregroundStyle(.clear) } Label(title: { Text("drag 'n' drop") }) { Image(systemName: "hand.pinch.fill") .foregroundStyle(.blue) } } .font(.largeTitle) } } #Preview { WelcomeView() } ================================================ FILE: approf/View/WkWrapper.swift ================================================ import AppKit import SwiftUI import WebKit struct WkWrapper: NSViewRepresentable { let wk: WKWebView @Bindable var wkState: WKState public func makeNSView(context: Context) -> WKWebView { wk.wantsLayer = true wk.navigationDelegate = context.coordinator wkState.url = wk.url return wk } func updateNSView(_ wk: WKWebView, context: Context) { wkState.url = wk.url } class Coordinator: NSObject, WKNavigationDelegate { var parent: WkWrapper init(_ parent: WkWrapper) { self.parent = parent } func webView(_ wk: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { parent.wkState.loading = true } func webView(_ wk: WKWebView, didFinish navigation: WKNavigation!) { parent.wkState.loading = false parent.wkState.url = wk.url // parent.wkState.updateStacks(with: webView) } func webView(_ wk: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { parent.wkState.loading = false // parent.wkState.updateStacks(with: webView) } } func makeCoordinator() -> Coordinator { Coordinator(self) } } ================================================ FILE: approf/WindowController.swift ================================================ import SwiftUI class CustomWindowController: NSWindowController { override func windowDidLoad() { super.windowDidLoad() window?.tabbingMode = .disallowed // Disable multi-tab. Not working actually. } } ================================================ FILE: approf/pprof.entitlements ================================================ ================================================ FILE: approf.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 70; objects = { /* Begin PBXBuildFile section */ EB0A27312C413CE400AD34ED /* NavigaionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB0A27302C413CE400AD34ED /* NavigaionView.swift */; }; EB1003E72C4A7EFC009E61F2 /* PopupView in Frameworks */ = {isa = PBXBuildFile; productRef = EB1003E62C4A7EFC009E61F2 /* PopupView */; }; EB1003E92C4A81D4009E61F2 /* FloatTopTrailing.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB1003E82C4A81D4009E61F2 /* FloatTopTrailing.swift */; }; EB1003EB2C4A8BC0009E61F2 /* CountDown.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB1003EA2C4A8BC0009E61F2 /* CountDown.swift */; }; EB1003ED2C4A9860009E61F2 /* NotificationFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB1003EC2C4A9860009E61F2 /* NotificationFeature.swift */; }; EB11A5162C4515C500E696DF /* WKState.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB11A5152C4515C500E696DF /* WKState.swift */; }; EB170FF32C40697100945866 /* Buttons.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB170FF22C40697100945866 /* Buttons.swift */; }; EB17F1F62C48169A00ED297A /* FileRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB17F1F52C48169A00ED297A /* FileRowView.swift */; }; EB1F1FBD2C4589A400FD585E /* pprof in pprrof executable */ = {isa = PBXBuildFile; fileRef = EB72830A2C44523F00B29D14 /* pprof */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; EB1F1FC12C45AEB700FD585E /* Shortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB1F1FC02C45AEB700FD585E /* Shortcuts.swift */; }; EB1F1FC32C45B2B300FD585E /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB1F1FC22C45B2B300FD585E /* Color.swift */; }; EB1F1FC52C45B38D00FD585E /* Color+Luminance.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB1F1FC42C45B38D00FD585E /* Color+Luminance.swift */; }; EB2619752C60AD0A007D4932 /* test-default-app.pb.gz in Resources */ = {isa = PBXBuildFile; fileRef = EB2619742C60AD0A007D4932 /* test-default-app.pb.gz */; }; EB2A0B4A2C4663CB0085609E /* CommandGenerate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB2A0B492C4663CB0085609E /* CommandGenerate.swift */; }; EB5026812C3D139F007C6282 /* Other.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB5026802C3D139F007C6282 /* Other.swift */; }; EB59362F2C4053D200088AB9 /* Others.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB59362E2C4053D200088AB9 /* Others.swift */; }; EB62B5812C5FE9FD00188A9D /* ImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB62B5802C5FE9FD00188A9D /* ImportView.swift */; }; EB62B5852C5FEB0700188A9D /* FileListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB62B5842C5FEB0700188A9D /* FileListView.swift */; }; EB62B5872C5FEB9200188A9D /* CommandPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB62B5862C5FEB9200188A9D /* CommandPreviewView.swift */; }; EB62B5892C5FEBC900188A9D /* TerminalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB62B5882C5FEBC900188A9D /* TerminalView.swift */; }; EB62B58B2C5FEC1B00188A9D /* ActionButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB62B58A2C5FEC1B00188A9D /* ActionButtonView.swift */; }; EB62B58F2C5FED3800188A9D /* ShortcutsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB62B58E2C5FED3800188A9D /* ShortcutsView.swift */; }; EB62B5922C5FEEE800188A9D /* PerviewData.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB62B5902C5FEEE800188A9D /* PerviewData.swift */; }; EB6530F52C41C09800A66B7C /* DropAndImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB6530F42C41C09800A66B7C /* DropAndImportView.swift */; }; EB6530FB2C41CF8D00A66B7C /* WebIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB6530FA2C41CF8D00A66B7C /* WebIndicatorView.swift */; }; EB659BC52C3EE91400F3F84E /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB659BC42C3EE91400F3F84E /* Command.swift */; }; EB7ADFC62C439CE500130909 /* WindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB7ADFC52C439CE500130909 /* WindowController.swift */; }; EB8190CA2C4571F8007EDE72 /* GradientBackgroundAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB8190C92C4571F8007EDE72 /* GradientBackgroundAnimation.swift */; }; EB85AB822C3DBA0A0080200A /* DetailFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB85AB812C3DBA0A0080200A /* DetailFeature.swift */; }; EB85AB852C3DCC2B0080200A /* LaunchingFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB85AB842C3DCC2B0080200A /* LaunchingFeature.swift */; }; EB85AB872C3DCC300080200A /* SuccessFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB85AB862C3DCC300080200A /* SuccessFeature.swift */; }; EB85AB892C3DCC670080200A /* FailureFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB85AB882C3DCC670080200A /* FailureFeature.swift */; }; EB85AB8D2C3E4D8C0080200A /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB85AB8C2C3E4D8C0080200A /* DetailView.swift */; }; EB85AB8F2C3E54CF0080200A /* WkWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB85AB8E2C3E54CF0080200A /* WkWrapper.swift */; }; EB85AB972C3E94DC0080200A /* IdleFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB85AB962C3E94DC0080200A /* IdleFeature.swift */; }; EB85AB992C3EAA410080200A /* SuccessView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB85AB982C3EAA410080200A /* SuccessView.swift */; }; EB8624A72C47A03B0010E153 /* Texts.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB8624A62C47A03B0010E153 /* Texts.swift */; }; EB8624A92C47B19D0010E153 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB8624A82C47B19D0010E153 /* File.swift */; }; EB8BADE32C6E8FA60036B81E /* TestAppDropFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB8BADE22C6E8FA60036B81E /* TestAppDropFiles.swift */; }; EB9355652C47E9D10055C90D /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB9355642C47E9D10055C90D /* StatusView.swift */; }; EB9355672C47F4E50055C90D /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB9355662C47F4E50055C90D /* Image.swift */; }; EB94442A2C42F6E50050F0D1 /* State.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB9444192C42F6E50050F0D1 /* State.swift */; }; EB94442C2C42F6E50050F0D1 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB94441B2C42F6E50050F0D1 /* AboutView.swift */; }; EB94442E2C42F6E50050F0D1 /* AppDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB9444222C42F6E50050F0D1 /* AppDataSource.swift */; }; EB94442F2C42F6E50050F0D1 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB9444252C42F6E50050F0D1 /* ContentView.swift */; }; EB9444302C42F6E50050F0D1 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB9444212C42F6E50050F0D1 /* App.swift */; }; EB9444312C42F6E50050F0D1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB9444232C42F6E50050F0D1 /* AppDelegate.swift */; }; EB9444332C42F6E50050F0D1 /* AppStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB9444182C42F6E50050F0D1 /* AppStorage.swift */; }; EB9444352C42F6E50050F0D1 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB94441F2C42F6E50050F0D1 /* WelcomeView.swift */; }; EB9444362C42F6E50050F0D1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EB9444242C42F6E50050F0D1 /* Assets.xcassets */; }; EB9444372C42F6E50050F0D1 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EB9444162C42F6E50050F0D1 /* Preview Assets.xcassets */; }; EB94443F2C42F70E0050F0D1 /* pprofUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB94443D2C42F70E0050F0D1 /* pprofUITestsLaunchTests.swift */; }; EB9444402C42F70E0050F0D1 /* pprofUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB94443C2C42F70E0050F0D1 /* pprofUITests.swift */; }; EB96D98D2C470E9C00FA6E52 /* UTH.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB96D98B2C470E9C00FA6E52 /* UTH.swift */; }; EB96D98E2C470E9C00FA6E52 /* UnderTheHoodView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB96D98C2C470E9C00FA6E52 /* UnderTheHoodView.swift */; }; EBAA59FE2C485162006CF326 /* Whatever.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBAA59FD2C485162006CF326 /* Whatever.swift */; }; EBB1899D2C430AD00037A8F4 /* ComposableArchitecture in Frameworks */ = {isa = PBXBuildFile; productRef = EBB1899C2C430AD00037A8F4 /* ComposableArchitecture */; }; EBB37A982C40EFCB00D04474 /* DetectingHTTPView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBB37A972C40EFCB00D04474 /* DetectingHTTPView.swift */; }; EBB37A9A2C40F7F700D04474 /* Symbols.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBB37A992C40F7F700D04474 /* Symbols.swift */; }; EBB794862C410F0900D5AE1C /* AppFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBB794852C410F0900D5AE1C /* AppFeature.swift */; }; EBB794882C41146000D5AE1C /* PProfRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBB794872C41146000D5AE1C /* PProfRowView.swift */; }; EBBB83212C60BD2A002F2892 /* DefaultGzAppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBBB83202C60BD2A002F2892 /* DefaultGzAppView.swift */; }; EBBB83232C60DB79002F2892 /* GraphvizGuideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBBB83222C60DB79002F2892 /* GraphvizGuideView.swift */; }; EBBB83272C60E4D6002F2892 /* NameView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBBB83262C60E4D6002F2892 /* NameView.swift */; }; EBBB83292C60E673002F2892 /* NameFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBBB83282C60E673002F2892 /* NameFeature.swift */; }; EBBB832B2C60EC3A002F2892 /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBBB832A2C60EC3A002F2892 /* Date.swift */; }; EBBC581A2C668A0F00A72918 /* TestDropFilesLegacy.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBBC58192C668A0F00A72918 /* TestDropFilesLegacy.swift */; }; EBBC581B2C668A0F00A72918 /* Dummy.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBBC58172C668A0F00A72918 /* Dummy.swift */; }; EBBC581C2C668A0F00A72918 /* b.pb in Resources */ = {isa = PBXBuildFile; fileRef = EBBC58162C668A0F00A72918 /* b.pb */; }; EBBC581D2C668A0F00A72918 /* a.pb.gz in Resources */ = {isa = PBXBuildFile; fileRef = EBBC58152C668A0F00A72918 /* a.pb.gz */; }; EBBC581F2C669A8100A72918 /* TestDropFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBBC581E2C669A8100A72918 /* TestDropFiles.swift */; }; EBC7C85E2C9DD31B00B330D3 /* DropAndAppendFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBC7C85D2C9DD31B00B330D3 /* DropAndAppendFeature.swift */; }; EBC7C8602C9DD32D00B330D3 /* DropAndAppendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBC7C85F2C9DD32D00B330D3 /* DropAndAppendView.swift */; }; EBD20FBD2C42D06300EE88D7 /* LightsOff.metal in Sources */ = {isa = PBXBuildFile; fileRef = EBD20FBC2C42D06300EE88D7 /* LightsOff.metal */; }; EBD292B42C430F8000FFB285 /* ShortcutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBD292B32C430F8000FFB285 /* ShortcutView.swift */; }; EBD292BA2C430F9700FFB285 /* KeyboardShortcuts in Frameworks */ = {isa = PBXBuildFile; productRef = EBD292B92C430F9700FFB285 /* KeyboardShortcuts */; }; EBD292BC2C43116400FFB285 /* SettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBD292BB2C43116400FFB285 /* SettingView.swift */; }; EBD292BF2C43168100FFB285 /* Pow in Frameworks */ = {isa = PBXBuildFile; productRef = EBD292BE2C43168100FFB285 /* Pow */; }; EBD293D32C43945000FFB285 /* AppShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBD293D22C43945000FFB285 /* AppShortcuts.swift */; }; EBDCE9162C46D71E00B7DE83 /* PProfBasic.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBDCE9152C46D71E00B7DE83 /* PProfBasic.swift */; }; EBDCE9182C46D78000B7DE83 /* PProfPresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBDCE9172C46D78000B7DE83 /* PProfPresentation.swift */; }; EBE8334C2C3C8AD400FD8A73 /* Hover.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBE8334B2C3C8AD400FD8A73 /* Hover.swift */; }; EBE8334F2C3C8BBD00FD8A73 /* Tooltip.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBE8334E2C3C8BBD00FD8A73 /* Tooltip.swift */; }; EBE833532C3C8D9500FD8A73 /* Ripple.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBE833522C3C8D9500FD8A73 /* Ripple.swift */; }; EBE833542C3C8D9500FD8A73 /* Ripple.metal in Sources */ = {isa = PBXBuildFile; fileRef = EBE833512C3C8D9500FD8A73 /* Ripple.metal */; }; EBEAAACA2C467DD60026C328 /* DropAndImportFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBEAAAC92C467DD60026C328 /* DropAndImportFeature.swift */; }; EBEC3ABB2C3D0F3C0016F878 /* Toolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBEC3ABA2C3D0F3C0016F878 /* Toolbar.swift */; }; EBEF3C002C3BBECB0003477D /* Exts.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBEF3BF72C3BBECB0003477D /* Exts.swift */; }; EBEF3C0B2C3BBF2A0003477D /* Logging in Frameworks */ = {isa = PBXBuildFile; productRef = EBEF3C0A2C3BBF2A0003477D /* Logging */; }; EBFC00EF2C3D0AFE004A1B93 /* Delay.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBFC00EE2C3D0AFE004A1B93 /* Delay.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ EBEF3A302C3B255B0003477D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = EBEF3A162C3B255A0003477D /* Project object */; proxyType = 1; remoteGlobalIDString = EBEF3A1D2C3B255A0003477D; remoteInfo = pprof; }; EBEF3A3A2C3B255B0003477D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = EBEF3A162C3B255A0003477D /* Project object */; proxyType = 1; remoteGlobalIDString = EBEF3A1D2C3B255A0003477D; remoteInfo = pprof; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ EB7282EA2C4443AE00B29D14 /* pprrof executable */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( EB1F1FBD2C4589A400FD585E /* pprof in pprrof executable */, ); name = "pprrof executable"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ EB0A27302C413CE400AD34ED /* NavigaionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigaionView.swift; sourceTree = ""; }; EB1003E82C4A81D4009E61F2 /* FloatTopTrailing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatTopTrailing.swift; sourceTree = ""; }; EB1003EA2C4A8BC0009E61F2 /* CountDown.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountDown.swift; sourceTree = ""; }; EB1003EC2C4A9860009E61F2 /* NotificationFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationFeature.swift; sourceTree = ""; }; EB11A5152C4515C500E696DF /* WKState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKState.swift; sourceTree = ""; }; EB170FF22C40697100945866 /* Buttons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Buttons.swift; sourceTree = ""; }; EB17F1F52C48169A00ED297A /* FileRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRowView.swift; sourceTree = ""; }; EB1F1FC02C45AEB700FD585E /* Shortcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shortcuts.swift; sourceTree = ""; }; EB1F1FC22C45B2B300FD585E /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; EB1F1FC42C45B38D00FD585E /* Color+Luminance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Luminance.swift"; sourceTree = ""; }; EB2619742C60AD0A007D4932 /* test-default-app.pb.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = "test-default-app.pb.gz"; sourceTree = ""; }; EB2A0B492C4663CB0085609E /* CommandGenerate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandGenerate.swift; sourceTree = ""; }; EB5026802C3D139F007C6282 /* Other.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Other.swift; sourceTree = ""; }; EB59362E2C4053D200088AB9 /* Others.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Others.swift; sourceTree = ""; }; EB62B5802C5FE9FD00188A9D /* ImportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportView.swift; sourceTree = ""; }; EB62B5842C5FEB0700188A9D /* FileListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileListView.swift; sourceTree = ""; }; EB62B5862C5FEB9200188A9D /* CommandPreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandPreviewView.swift; sourceTree = ""; }; EB62B5882C5FEBC900188A9D /* TerminalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalView.swift; sourceTree = ""; }; EB62B58A2C5FEC1B00188A9D /* ActionButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionButtonView.swift; sourceTree = ""; }; EB62B58E2C5FED3800188A9D /* ShortcutsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsView.swift; sourceTree = ""; }; EB62B5902C5FEEE800188A9D /* PerviewData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PerviewData.swift; sourceTree = ""; }; EB6530F42C41C09800A66B7C /* DropAndImportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropAndImportView.swift; sourceTree = ""; }; EB6530FA2C41CF8D00A66B7C /* WebIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebIndicatorView.swift; sourceTree = ""; }; EB659BC42C3EE91400F3F84E /* Command.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = ""; }; EB6E4C072C45424B005C1225 /* ci_post_clone.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = ci_post_clone.sh; sourceTree = ""; }; EB6E4C092C4542AF005C1225 /* macros.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = macros.json; sourceTree = ""; }; EB72830A2C44523F00B29D14 /* pprof */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = pprof; sourceTree = ""; }; EB7ADFC52C439CE500130909 /* WindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowController.swift; sourceTree = ""; }; EB8190C92C4571F8007EDE72 /* GradientBackgroundAnimation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradientBackgroundAnimation.swift; sourceTree = ""; }; EB85AB812C3DBA0A0080200A /* DetailFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailFeature.swift; sourceTree = ""; }; EB85AB842C3DCC2B0080200A /* LaunchingFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchingFeature.swift; sourceTree = ""; }; EB85AB862C3DCC300080200A /* SuccessFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuccessFeature.swift; sourceTree = ""; }; EB85AB882C3DCC670080200A /* FailureFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailureFeature.swift; sourceTree = ""; }; EB85AB8C2C3E4D8C0080200A /* DetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailView.swift; sourceTree = ""; }; EB85AB8E2C3E54CF0080200A /* WkWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WkWrapper.swift; sourceTree = ""; }; EB85AB962C3E94DC0080200A /* IdleFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdleFeature.swift; sourceTree = ""; }; EB85AB982C3EAA410080200A /* SuccessView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuccessView.swift; sourceTree = ""; }; EB8624A62C47A03B0010E153 /* Texts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Texts.swift; sourceTree = ""; }; EB8624A82C47B19D0010E153 /* File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = ""; }; EB8BADE22C6E8FA60036B81E /* TestAppDropFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestAppDropFiles.swift; sourceTree = ""; }; EB9355642C47E9D10055C90D /* StatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusView.swift; sourceTree = ""; }; EB9355662C47F4E50055C90D /* Image.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Image.swift; sourceTree = ""; }; EB9444162C42F6E50050F0D1 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; EB9444182C42F6E50050F0D1 /* AppStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStorage.swift; sourceTree = ""; }; EB9444192C42F6E50050F0D1 /* State.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = State.swift; sourceTree = ""; }; EB94441B2C42F6E50050F0D1 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = ""; }; EB94441F2C42F6E50050F0D1 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = ""; }; EB9444212C42F6E50050F0D1 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; EB9444222C42F6E50050F0D1 /* AppDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDataSource.swift; sourceTree = ""; }; EB9444232C42F6E50050F0D1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; EB9444242C42F6E50050F0D1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; EB9444252C42F6E50050F0D1 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; EB9444272C42F6E50050F0D1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; EB9444282C42F6E50050F0D1 /* pprof.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = pprof.entitlements; sourceTree = ""; }; EB94443C2C42F70E0050F0D1 /* pprofUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = pprofUITests.swift; sourceTree = ""; }; EB94443D2C42F70E0050F0D1 /* pprofUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = pprofUITestsLaunchTests.swift; sourceTree = ""; }; EB96D98B2C470E9C00FA6E52 /* UTH.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTH.swift; sourceTree = ""; }; EB96D98C2C470E9C00FA6E52 /* UnderTheHoodView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnderTheHoodView.swift; sourceTree = ""; }; EBAA59FD2C485162006CF326 /* Whatever.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Whatever.swift; sourceTree = ""; }; EBB37A972C40EFCB00D04474 /* DetectingHTTPView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetectingHTTPView.swift; sourceTree = ""; }; EBB37A992C40F7F700D04474 /* Symbols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Symbols.swift; sourceTree = ""; }; EBB794852C410F0900D5AE1C /* AppFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppFeature.swift; sourceTree = ""; }; EBB794872C41146000D5AE1C /* PProfRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PProfRowView.swift; sourceTree = ""; }; EBBB83202C60BD2A002F2892 /* DefaultGzAppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultGzAppView.swift; sourceTree = ""; }; EBBB83222C60DB79002F2892 /* GraphvizGuideView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphvizGuideView.swift; sourceTree = ""; }; EBBB83262C60E4D6002F2892 /* NameView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NameView.swift; sourceTree = ""; }; EBBB83282C60E673002F2892 /* NameFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NameFeature.swift; sourceTree = ""; }; EBBB832A2C60EC3A002F2892 /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = ""; }; EBBC58152C668A0F00A72918 /* a.pb.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = a.pb.gz; sourceTree = ""; }; EBBC58162C668A0F00A72918 /* b.pb */ = {isa = PBXFileReference; lastKnownFileType = file; path = b.pb; sourceTree = ""; }; EBBC58172C668A0F00A72918 /* Dummy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dummy.swift; sourceTree = ""; }; EBBC58192C668A0F00A72918 /* TestDropFilesLegacy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestDropFilesLegacy.swift; sourceTree = ""; }; EBBC581E2C669A8100A72918 /* TestDropFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestDropFiles.swift; sourceTree = ""; }; EBBC58202C669B0500A72918 /* TestPlan.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = TestPlan.xctestplan; path = approfTests/TestPlan.xctestplan; sourceTree = ""; }; EBC7C85D2C9DD31B00B330D3 /* DropAndAppendFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropAndAppendFeature.swift; sourceTree = ""; }; EBC7C85F2C9DD32D00B330D3 /* DropAndAppendView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropAndAppendView.swift; sourceTree = ""; }; EBD20FBC2C42D06300EE88D7 /* LightsOff.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = LightsOff.metal; sourceTree = ""; }; EBD292B32C430F8000FFB285 /* ShortcutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutView.swift; sourceTree = ""; }; EBD292BB2C43116400FFB285 /* SettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingView.swift; sourceTree = ""; }; EBD293D22C43945000FFB285 /* AppShortcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppShortcuts.swift; sourceTree = ""; }; EBDCE9152C46D71E00B7DE83 /* PProfBasic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PProfBasic.swift; sourceTree = ""; }; EBDCE9172C46D78000B7DE83 /* PProfPresentation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PProfPresentation.swift; sourceTree = ""; }; EBE8334B2C3C8AD400FD8A73 /* Hover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Hover.swift; sourceTree = ""; }; EBE8334E2C3C8BBD00FD8A73 /* Tooltip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tooltip.swift; sourceTree = ""; }; EBE833512C3C8D9500FD8A73 /* Ripple.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Ripple.metal; sourceTree = ""; }; EBE833522C3C8D9500FD8A73 /* Ripple.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ripple.swift; sourceTree = ""; }; EBEAAAC92C467DD60026C328 /* DropAndImportFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropAndImportFeature.swift; sourceTree = ""; }; EBEC3ABA2C3D0F3C0016F878 /* Toolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toolbar.swift; sourceTree = ""; }; EBEF3A1E2C3B255A0003477D /* approf.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = approf.app; sourceTree = BUILT_PRODUCTS_DIR; }; EBEF3A2F2C3B255B0003477D /* approfTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = approfTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; EBEF3A392C3B255B0003477D /* approfUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = approfUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; EBEF3BF72C3BBECB0003477D /* Exts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Exts.swift; sourceTree = ""; }; EBEF3BFC2C3BBECB0003477D /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; EBFC00EE2C3D0AFE004A1B93 /* Delay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Delay.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ EB7D79402C65635200CCD4F3 /* .github */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = .github; sourceTree = ""; }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ EBEF3A1B2C3B255A0003477D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( EB1003E72C4A7EFC009E61F2 /* PopupView in Frameworks */, EBD292BF2C43168100FFB285 /* Pow in Frameworks */, EBD292BA2C430F9700FFB285 /* KeyboardShortcuts in Frameworks */, EBB1899D2C430AD00037A8F4 /* ComposableArchitecture in Frameworks */, EBEF3C0B2C3BBF2A0003477D /* Logging in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; EBEF3A2C2C3B255B0003477D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; EBEF3A362C3B255B0003477D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ EB13E2C12C499E5E00484479 /* Setting */ = { isa = PBXGroup; children = ( EBD292BB2C43116400FFB285 /* SettingView.swift */, EBBB83222C60DB79002F2892 /* GraphvizGuideView.swift */, EBBB83202C60BD2A002F2892 /* DefaultGzAppView.swift */, ); path = Setting; sourceTree = ""; }; EB170FF12C40696600945866 /* Component */ = { isa = PBXGroup; children = ( EB1003EA2C4A8BC0009E61F2 /* CountDown.swift */, EB1003E82C4A81D4009E61F2 /* FloatTopTrailing.swift */, EB8624A62C47A03B0010E153 /* Texts.swift */, EB170FF22C40697100945866 /* Buttons.swift */, EBB37A992C40F7F700D04474 /* Symbols.swift */, EBE833502C3C8D7F00FD8A73 /* effcts */, ); path = Component; sourceTree = ""; }; EB2A0B482C4663A10085609E /* App */ = { isa = PBXGroup; children = ( EBD293D22C43945000FFB285 /* AppShortcuts.swift */, EB9444222C42F6E50050F0D1 /* AppDataSource.swift */, EB9444232C42F6E50050F0D1 /* AppDelegate.swift */, ); path = App; sourceTree = ""; }; EB62B5912C5FEEE800188A9D /* Preview */ = { isa = PBXGroup; children = ( EB62B5902C5FEEE800188A9D /* PerviewData.swift */, ); path = Preview; sourceTree = ""; }; EB6E4C082C45424B005C1225 /* ci_scripts */ = { isa = PBXGroup; children = ( EB6E4C072C45424B005C1225 /* ci_post_clone.sh */, EB6E4C092C4542AF005C1225 /* macros.json */, ); path = ci_scripts; sourceTree = ""; }; EB8190EE2C45852F007EDE72 /* DragNDrop */ = { isa = PBXGroup; children = ( EBEAAAC92C467DD60026C328 /* DropAndImportFeature.swift */, EB6530F42C41C09800A66B7C /* DropAndImportView.swift */, EBC7C85D2C9DD31B00B330D3 /* DropAndAppendFeature.swift */, EBC7C85F2C9DD32D00B330D3 /* DropAndAppendView.swift */, EB62B5802C5FE9FD00188A9D /* ImportView.swift */, ); path = DragNDrop; sourceTree = ""; }; EB9444172C42F6E50050F0D1 /* Preview Content */ = { isa = PBXGroup; children = ( EB9444162C42F6E50050F0D1 /* Preview Assets.xcassets */, ); path = "Preview Content"; sourceTree = ""; }; EB94441A2C42F6E50050F0D1 /* State */ = { isa = PBXGroup; children = ( EB9444182C42F6E50050F0D1 /* AppStorage.swift */, EB9444192C42F6E50050F0D1 /* State.swift */, EB11A5152C4515C500E696DF /* WKState.swift */, ); path = State; sourceTree = ""; }; EB9444202C42F6E50050F0D1 /* View */ = { isa = PBXGroup; children = ( EB13E2C12C499E5E00484479 /* Setting */, EB8190EE2C45852F007EDE72 /* DragNDrop */, EBB794852C410F0900D5AE1C /* AppFeature.swift */, EB0A27302C413CE400AD34ED /* NavigaionView.swift */, EB85AB812C3DBA0A0080200A /* DetailFeature.swift */, EB85AB8C2C3E4D8C0080200A /* DetailView.swift */, EB85AB962C3E94DC0080200A /* IdleFeature.swift */, EB85AB842C3DCC2B0080200A /* LaunchingFeature.swift */, EB96D9882C470E5600FA6E52 /* UnderTheHood */, EBB37A972C40EFCB00D04474 /* DetectingHTTPView.swift */, EB85AB862C3DCC300080200A /* SuccessFeature.swift */, EB85AB982C3EAA410080200A /* SuccessView.swift */, EB6530FA2C41CF8D00A66B7C /* WebIndicatorView.swift */, EB85AB882C3DCC670080200A /* FailureFeature.swift */, EB85AB8E2C3E54CF0080200A /* WkWrapper.swift */, EBB794872C41146000D5AE1C /* PProfRowView.swift */, EB94441B2C42F6E50050F0D1 /* AboutView.swift */, EB94441F2C42F6E50050F0D1 /* WelcomeView.swift */, EBD292B32C430F8000FFB285 /* ShortcutView.swift */, ); path = View; sourceTree = ""; }; EB9444292C42F6E50050F0D1 /* approf */ = { isa = PBXGroup; children = ( EB9444212C42F6E50050F0D1 /* App.swift */, EB2A0B482C4663A10085609E /* App */, EB7ADFC52C439CE500130909 /* WindowController.swift */, EB9444252C42F6E50050F0D1 /* ContentView.swift */, EBDCE9142C46D70400B7DE83 /* Types */, EB94441A2C42F6E50050F0D1 /* State */, EB9444202C42F6E50050F0D1 /* View */, EBD292B22C430E8B00FFB285 /* Process */, EB170FF12C40696600945866 /* Component */, EBE8334D2C3C8AE900FD8A73 /* SwiftUI AppKit */, EB62B5912C5FEEE800188A9D /* Preview */, EBEF3BF72C3BBECB0003477D /* Exts.swift */, EB9444242C42F6E50050F0D1 /* Assets.xcassets */, EB9444272C42F6E50050F0D1 /* Info.plist */, EB9444282C42F6E50050F0D1 /* pprof.entitlements */, EB9444172C42F6E50050F0D1 /* Preview Content */, ); path = approf; sourceTree = ""; }; EB94443A2C42F7050050F0D1 /* approfTests */ = { isa = PBXGroup; children = ( EBBC58182C668A0F00A72918 /* Files */, EBBC58192C668A0F00A72918 /* TestDropFilesLegacy.swift */, EBBC581E2C669A8100A72918 /* TestDropFiles.swift */, EB8BADE22C6E8FA60036B81E /* TestAppDropFiles.swift */, ); path = approfTests; sourceTree = ""; }; EB94443E2C42F70E0050F0D1 /* approfUITests */ = { isa = PBXGroup; children = ( EB94443C2C42F70E0050F0D1 /* pprofUITests.swift */, EB94443D2C42F70E0050F0D1 /* pprofUITestsLaunchTests.swift */, ); path = approfUITests; sourceTree = ""; }; EB96D9882C470E5600FA6E52 /* UnderTheHood */ = { isa = PBXGroup; children = ( EB96D98B2C470E9C00FA6E52 /* UTH.swift */, EB96D98C2C470E9C00FA6E52 /* UnderTheHoodView.swift */, EB62B58E2C5FED3800188A9D /* ShortcutsView.swift */, EB62B58A2C5FEC1B00188A9D /* ActionButtonView.swift */, EB62B5882C5FEBC900188A9D /* TerminalView.swift */, EB62B5842C5FEB0700188A9D /* FileListView.swift */, EBBB83262C60E4D6002F2892 /* NameView.swift */, EBBB83282C60E673002F2892 /* NameFeature.swift */, EB17F1F52C48169A00ED297A /* FileRowView.swift */, EB62B5862C5FEB9200188A9D /* CommandPreviewView.swift */, EB9355642C47E9D10055C90D /* StatusView.swift */, EB1003EC2C4A9860009E61F2 /* NotificationFeature.swift */, ); path = UnderTheHood; sourceTree = ""; }; EBBC58182C668A0F00A72918 /* Files */ = { isa = PBXGroup; children = ( EBBC58152C668A0F00A72918 /* a.pb.gz */, EBBC58162C668A0F00A72918 /* b.pb */, EBBC58172C668A0F00A72918 /* Dummy.swift */, ); path = Files; sourceTree = ""; }; EBD292B22C430E8B00FFB285 /* Process */ = { isa = PBXGroup; children = ( EB2619742C60AD0A007D4932 /* test-default-app.pb.gz */, EB2A0B492C4663CB0085609E /* CommandGenerate.swift */, EB659BC42C3EE91400F3F84E /* Command.swift */, EBEF3BFC2C3BBECB0003477D /* README.md */, EB72830A2C44523F00B29D14 /* pprof */, EBAA59FD2C485162006CF326 /* Whatever.swift */, ); path = Process; sourceTree = ""; }; EBDCE9142C46D70400B7DE83 /* Types */ = { isa = PBXGroup; children = ( EBDCE9152C46D71E00B7DE83 /* PProfBasic.swift */, EBDCE9172C46D78000B7DE83 /* PProfPresentation.swift */, EB59362E2C4053D200088AB9 /* Others.swift */, ); path = Types; sourceTree = ""; }; EBE8334D2C3C8AE900FD8A73 /* SwiftUI AppKit */ = { isa = PBXGroup; children = ( EBE8334B2C3C8AD400FD8A73 /* Hover.swift */, EBE8334E2C3C8BBD00FD8A73 /* Tooltip.swift */, EBFC00EE2C3D0AFE004A1B93 /* Delay.swift */, EBBB832A2C60EC3A002F2892 /* Date.swift */, EBEC3ABA2C3D0F3C0016F878 /* Toolbar.swift */, EB5026802C3D139F007C6282 /* Other.swift */, EB1F1FC02C45AEB700FD585E /* Shortcuts.swift */, EB1F1FC22C45B2B300FD585E /* Color.swift */, EB1F1FC42C45B38D00FD585E /* Color+Luminance.swift */, EB8624A82C47B19D0010E153 /* File.swift */, EB9355662C47F4E50055C90D /* Image.swift */, ); path = "SwiftUI AppKit"; sourceTree = ""; }; EBE833502C3C8D7F00FD8A73 /* effcts */ = { isa = PBXGroup; children = ( EB8190C92C4571F8007EDE72 /* GradientBackgroundAnimation.swift */, EBE833512C3C8D9500FD8A73 /* Ripple.metal */, EBD20FBC2C42D06300EE88D7 /* LightsOff.metal */, EBE833522C3C8D9500FD8A73 /* Ripple.swift */, ); path = effcts; sourceTree = ""; }; EBEF3A152C3B255A0003477D = { isa = PBXGroup; children = ( EBBC58202C669B0500A72918 /* TestPlan.xctestplan */, EB7D79402C65635200CCD4F3 /* .github */, EB9444292C42F6E50050F0D1 /* approf */, EB94443A2C42F7050050F0D1 /* approfTests */, EB94443E2C42F70E0050F0D1 /* approfUITests */, EBEF3A1F2C3B255A0003477D /* Products */, EBEF3C092C3BBF2A0003477D /* Frameworks */, EB6E4C082C45424B005C1225 /* ci_scripts */, ); sourceTree = ""; }; EBEF3A1F2C3B255A0003477D /* Products */ = { isa = PBXGroup; children = ( EBEF3A1E2C3B255A0003477D /* approf.app */, EBEF3A2F2C3B255B0003477D /* approfTests.xctest */, EBEF3A392C3B255B0003477D /* approfUITests.xctest */, ); name = Products; sourceTree = ""; }; EBEF3C092C3BBF2A0003477D /* Frameworks */ = { isa = PBXGroup; children = ( ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ EBEF3A1D2C3B255A0003477D /* approf */ = { isa = PBXNativeTarget; buildConfigurationList = EBEF3A432C3B255B0003477D /* Build configuration list for PBXNativeTarget "approf" */; buildPhases = ( EBEF3A1A2C3B255A0003477D /* Sources */, EBEF3A1B2C3B255A0003477D /* Frameworks */, EBEF3A1C2C3B255A0003477D /* Resources */, EB7282EA2C4443AE00B29D14 /* pprrof executable */, ); buildRules = ( ); dependencies = ( ); name = approf; packageProductDependencies = ( EBEF3C0A2C3BBF2A0003477D /* Logging */, EBB1899C2C430AD00037A8F4 /* ComposableArchitecture */, EBD292B92C430F9700FFB285 /* KeyboardShortcuts */, EBD292BE2C43168100FFB285 /* Pow */, EB1003E62C4A7EFC009E61F2 /* PopupView */, ); productName = pprof; productReference = EBEF3A1E2C3B255A0003477D /* approf.app */; productType = "com.apple.product-type.application"; }; EBEF3A2E2C3B255B0003477D /* approfTests */ = { isa = PBXNativeTarget; buildConfigurationList = EBEF3A462C3B255B0003477D /* Build configuration list for PBXNativeTarget "approfTests" */; buildPhases = ( EBEF3A2B2C3B255B0003477D /* Sources */, EBEF3A2C2C3B255B0003477D /* Frameworks */, EBEF3A2D2C3B255B0003477D /* Resources */, ); buildRules = ( ); dependencies = ( EBEF3A312C3B255B0003477D /* PBXTargetDependency */, ); name = approfTests; productName = pprofTests; productReference = EBEF3A2F2C3B255B0003477D /* approfTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; EBEF3A382C3B255B0003477D /* approfUITests */ = { isa = PBXNativeTarget; buildConfigurationList = EBEF3A492C3B255B0003477D /* Build configuration list for PBXNativeTarget "approfUITests" */; buildPhases = ( EBEF3A352C3B255B0003477D /* Sources */, EBEF3A362C3B255B0003477D /* Frameworks */, EBEF3A372C3B255B0003477D /* Resources */, ); buildRules = ( ); dependencies = ( EBEF3A3B2C3B255B0003477D /* PBXTargetDependency */, ); name = approfUITests; productName = pprofUITests; productReference = EBEF3A392C3B255B0003477D /* approfUITests.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ EBEF3A162C3B255A0003477D /* Project object */ = { isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1600; LastUpgradeCheck = 1600; TargetAttributes = { EBEF3A1D2C3B255A0003477D = { CreatedOnToolsVersion = 16.0; }; EBEF3A2E2C3B255B0003477D = { CreatedOnToolsVersion = 16.0; TestTargetID = EBEF3A1D2C3B255A0003477D; }; EBEF3A382C3B255B0003477D = { CreatedOnToolsVersion = 16.0; TestTargetID = EBEF3A1D2C3B255A0003477D; }; }; }; buildConfigurationList = EBEF3A192C3B255A0003477D /* Build configuration list for PBXProject "approf" */; compatibilityVersion = "Xcode 15.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = EBEF3A152C3B255A0003477D; packageReferences = ( EBEF3C072C3BBEF80003477D /* XCRemoteSwiftPackageReference "swift-log" */, EB3BC5B92C430A6700C9920F /* XCRemoteSwiftPackageReference "swift-composable-architecture" */, EBD292B12C430D5800FFB285 /* XCRemoteSwiftPackageReference "KeyboardShortcuts" */, EBD292BD2C43151A00FFB285 /* XCRemoteSwiftPackageReference "Pow" */, EB1003E52C4A7EEC009E61F2 /* XCRemoteSwiftPackageReference "PopupView" */, ); productRefGroup = EBEF3A1F2C3B255A0003477D /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( EBEF3A1D2C3B255A0003477D /* approf */, EBEF3A2E2C3B255B0003477D /* approfTests */, EBEF3A382C3B255B0003477D /* approfUITests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ EBEF3A1C2C3B255A0003477D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( EB9444362C42F6E50050F0D1 /* Assets.xcassets in Resources */, EB9444372C42F6E50050F0D1 /* Preview Assets.xcassets in Resources */, EB2619752C60AD0A007D4932 /* test-default-app.pb.gz in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; EBEF3A2D2C3B255B0003477D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( EBBC581C2C668A0F00A72918 /* b.pb in Resources */, EBBC581D2C668A0F00A72918 /* a.pb.gz in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; EBEF3A372C3B255B0003477D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ EBEF3A1A2C3B255A0003477D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( EB0A27312C413CE400AD34ED /* NavigaionView.swift in Sources */, EBD292B42C430F8000FFB285 /* ShortcutView.swift in Sources */, EBC7C8602C9DD32D00B330D3 /* DropAndAppendView.swift in Sources */, EB6530FB2C41CF8D00A66B7C /* WebIndicatorView.swift in Sources */, EBBB832B2C60EC3A002F2892 /* Date.swift in Sources */, EBEC3ABB2C3D0F3C0016F878 /* Toolbar.swift in Sources */, EB94442A2C42F6E50050F0D1 /* State.swift in Sources */, EB2A0B4A2C4663CB0085609E /* CommandGenerate.swift in Sources */, EBDCE9182C46D78000B7DE83 /* PProfPresentation.swift in Sources */, EB94442C2C42F6E50050F0D1 /* AboutView.swift in Sources */, EB11A5162C4515C500E696DF /* WKState.swift in Sources */, EB62B5872C5FEB9200188A9D /* CommandPreviewView.swift in Sources */, EB1F1FC12C45AEB700FD585E /* Shortcuts.swift in Sources */, EB1003EB2C4A8BC0009E61F2 /* CountDown.swift in Sources */, EB94442E2C42F6E50050F0D1 /* AppDataSource.swift in Sources */, EB94442F2C42F6E50050F0D1 /* ContentView.swift in Sources */, EB9355652C47E9D10055C90D /* StatusView.swift in Sources */, EB9444302C42F6E50050F0D1 /* App.swift in Sources */, EB9444312C42F6E50050F0D1 /* AppDelegate.swift in Sources */, EB9444332C42F6E50050F0D1 /* AppStorage.swift in Sources */, EB9444352C42F6E50050F0D1 /* WelcomeView.swift in Sources */, EBDCE9162C46D71E00B7DE83 /* PProfBasic.swift in Sources */, EB170FF32C40697100945866 /* Buttons.swift in Sources */, EBBB83212C60BD2A002F2892 /* DefaultGzAppView.swift in Sources */, EB8624A72C47A03B0010E153 /* Texts.swift in Sources */, EB85AB8D2C3E4D8C0080200A /* DetailView.swift in Sources */, EB85AB872C3DCC300080200A /* SuccessFeature.swift in Sources */, EB96D98D2C470E9C00FA6E52 /* UTH.swift in Sources */, EB1003E92C4A81D4009E61F2 /* FloatTopTrailing.swift in Sources */, EB9355672C47F4E50055C90D /* Image.swift in Sources */, EB96D98E2C470E9C00FA6E52 /* UnderTheHoodView.swift in Sources */, EBB794862C410F0900D5AE1C /* AppFeature.swift in Sources */, EBE8334F2C3C8BBD00FD8A73 /* Tooltip.swift in Sources */, EB62B58B2C5FEC1B00188A9D /* ActionButtonView.swift in Sources */, EB62B5892C5FEBC900188A9D /* TerminalView.swift in Sources */, EBD20FBD2C42D06300EE88D7 /* LightsOff.metal in Sources */, EB85AB822C3DBA0A0080200A /* DetailFeature.swift in Sources */, EBD292BC2C43116400FFB285 /* SettingView.swift in Sources */, EBAA59FE2C485162006CF326 /* Whatever.swift in Sources */, EBEF3C002C3BBECB0003477D /* Exts.swift in Sources */, EBE833532C3C8D9500FD8A73 /* Ripple.swift in Sources */, EBD293D32C43945000FFB285 /* AppShortcuts.swift in Sources */, EB62B5852C5FEB0700188A9D /* FileListView.swift in Sources */, EBEAAACA2C467DD60026C328 /* DropAndImportFeature.swift in Sources */, EBB37A982C40EFCB00D04474 /* DetectingHTTPView.swift in Sources */, EB8190CA2C4571F8007EDE72 /* GradientBackgroundAnimation.swift in Sources */, EB17F1F62C48169A00ED297A /* FileRowView.swift in Sources */, EBBB83272C60E4D6002F2892 /* NameView.swift in Sources */, EB62B5922C5FEEE800188A9D /* PerviewData.swift in Sources */, EB85AB8F2C3E54CF0080200A /* WkWrapper.swift in Sources */, EBE833542C3C8D9500FD8A73 /* Ripple.metal in Sources */, EB7ADFC62C439CE500130909 /* WindowController.swift in Sources */, EBBB83232C60DB79002F2892 /* GraphvizGuideView.swift in Sources */, EBB37A9A2C40F7F700D04474 /* Symbols.swift in Sources */, EB59362F2C4053D200088AB9 /* Others.swift in Sources */, EBE8334C2C3C8AD400FD8A73 /* Hover.swift in Sources */, EB62B58F2C5FED3800188A9D /* ShortcutsView.swift in Sources */, EBC7C85E2C9DD31B00B330D3 /* DropAndAppendFeature.swift in Sources */, EBBB83292C60E673002F2892 /* NameFeature.swift in Sources */, EBB794882C41146000D5AE1C /* PProfRowView.swift in Sources */, EB1F1FC32C45B2B300FD585E /* Color.swift in Sources */, EB62B5812C5FE9FD00188A9D /* ImportView.swift in Sources */, EB85AB972C3E94DC0080200A /* IdleFeature.swift in Sources */, EB5026812C3D139F007C6282 /* Other.swift in Sources */, EB659BC52C3EE91400F3F84E /* Command.swift in Sources */, EB85AB992C3EAA410080200A /* SuccessView.swift in Sources */, EB85AB892C3DCC670080200A /* FailureFeature.swift in Sources */, EB1003ED2C4A9860009E61F2 /* NotificationFeature.swift in Sources */, EB1F1FC52C45B38D00FD585E /* Color+Luminance.swift in Sources */, EB6530F52C41C09800A66B7C /* DropAndImportView.swift in Sources */, EB85AB852C3DCC2B0080200A /* LaunchingFeature.swift in Sources */, EB8624A92C47B19D0010E153 /* File.swift in Sources */, EBFC00EF2C3D0AFE004A1B93 /* Delay.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; EBEF3A2B2C3B255B0003477D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( EBBC581A2C668A0F00A72918 /* TestDropFilesLegacy.swift in Sources */, EBBC581B2C668A0F00A72918 /* Dummy.swift in Sources */, EB8BADE32C6E8FA60036B81E /* TestAppDropFiles.swift in Sources */, EBBC581F2C669A8100A72918 /* TestDropFiles.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; EBEF3A352C3B255B0003477D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( EB94443F2C42F70E0050F0D1 /* pprofUITestsLaunchTests.swift in Sources */, EB9444402C42F70E0050F0D1 /* pprofUITests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ EBEF3A312C3B255B0003477D /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = EBEF3A1D2C3B255A0003477D /* approf */; targetProxy = EBEF3A302C3B255B0003477D /* PBXContainerItemProxy */; }; EBEF3A3B2C3B255B0003477D /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = EBEF3A1D2C3B255A0003477D /* approf */; targetProxy = EBEF3A3A2C3B255B0003477D /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ EBEF3A412C3B255B0003477D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MACOSX_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; EBEF3A422C3B255B0003477D /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MACOSX_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; }; name = Release; }; EBEF3A442C3B255B0003477D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = approf/pprof.entitlements; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"approf/Preview Content\""; DEVELOPMENT_TEAM = 46PN73H4K6; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = approf/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = approf; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/approf/Process", ); MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 14.1.3; PRODUCT_BUNDLE_IDENTIFIER = the.future.app.approf.approf; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; }; name = Debug; }; EBEF3A452C3B255B0003477D /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = approf/pprof.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"approf/Preview Content\""; DEVELOPMENT_TEAM = 46PN73H4K6; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = approf/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = approf; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/approf/Process", ); MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 14.1.3; PRODUCT_BUNDLE_IDENTIFIER = the.future.app.approf.approf; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; }; name = Release; }; EBEF3A472C3B255B0003477D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 46PN73H4K6; GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = the.future.app.pprof.pprofTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/approf.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/approf"; }; name = Debug; }; EBEF3A482C3B255B0003477D /* Release */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 46PN73H4K6; GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = the.future.app.pprof.pprofTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/approf.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/approf"; }; name = Release; }; EBEF3A4A2C3B255B0003477D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 46PN73H4K6; GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = the.future.app.pprof.pprofUITests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TEST_TARGET_NAME = pprof; }; name = Debug; }; EBEF3A4B2C3B255B0003477D /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 46PN73H4K6; GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = the.future.app.pprof.pprofUITests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TEST_TARGET_NAME = pprof; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ EBEF3A192C3B255A0003477D /* Build configuration list for PBXProject "approf" */ = { isa = XCConfigurationList; buildConfigurations = ( EBEF3A412C3B255B0003477D /* Debug */, EBEF3A422C3B255B0003477D /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; EBEF3A432C3B255B0003477D /* Build configuration list for PBXNativeTarget "approf" */ = { isa = XCConfigurationList; buildConfigurations = ( EBEF3A442C3B255B0003477D /* Debug */, EBEF3A452C3B255B0003477D /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; EBEF3A462C3B255B0003477D /* Build configuration list for PBXNativeTarget "approfTests" */ = { isa = XCConfigurationList; buildConfigurations = ( EBEF3A472C3B255B0003477D /* Debug */, EBEF3A482C3B255B0003477D /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; EBEF3A492C3B255B0003477D /* Build configuration list for PBXNativeTarget "approfUITests" */ = { isa = XCConfigurationList; buildConfigurations = ( EBEF3A4A2C3B255B0003477D /* Debug */, EBEF3A4B2C3B255B0003477D /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ EB1003E52C4A7EEC009E61F2 /* XCRemoteSwiftPackageReference "PopupView" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/exyte/PopupView.git"; requirement = { kind = upToNextMajorVersion; minimumVersion = 3.0.4; }; }; EB3BC5B92C430A6700C9920F /* XCRemoteSwiftPackageReference "swift-composable-architecture" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/pointfreeco/swift-composable-architecture"; requirement = { kind = exactVersion; version = 1.14.0; }; }; EBD292B12C430D5800FFB285 /* XCRemoteSwiftPackageReference "KeyboardShortcuts" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/sindresorhus/KeyboardShortcuts"; requirement = { kind = upToNextMajorVersion; minimumVersion = 2.0.1; }; }; EBD292BD2C43151A00FFB285 /* XCRemoteSwiftPackageReference "Pow" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/EmergeTools/Pow.git"; requirement = { kind = upToNextMajorVersion; minimumVersion = 1.0.4; }; }; EBEF3C072C3BBEF80003477D /* XCRemoteSwiftPackageReference "swift-log" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/apple/swift-log"; requirement = { kind = upToNextMajorVersion; minimumVersion = 1.6.1; }; }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ EB1003E62C4A7EFC009E61F2 /* PopupView */ = { isa = XCSwiftPackageProductDependency; package = EB1003E52C4A7EEC009E61F2 /* XCRemoteSwiftPackageReference "PopupView" */; productName = PopupView; }; EBB1899C2C430AD00037A8F4 /* ComposableArchitecture */ = { isa = XCSwiftPackageProductDependency; package = EB3BC5B92C430A6700C9920F /* XCRemoteSwiftPackageReference "swift-composable-architecture" */; productName = ComposableArchitecture; }; EBD292B92C430F9700FFB285 /* KeyboardShortcuts */ = { isa = XCSwiftPackageProductDependency; package = EBD292B12C430D5800FFB285 /* XCRemoteSwiftPackageReference "KeyboardShortcuts" */; productName = KeyboardShortcuts; }; EBD292BE2C43168100FFB285 /* Pow */ = { isa = XCSwiftPackageProductDependency; package = EBD292BD2C43151A00FFB285 /* XCRemoteSwiftPackageReference "Pow" */; productName = Pow; }; EBEF3C0A2C3BBF2A0003477D /* Logging */ = { isa = XCSwiftPackageProductDependency; package = EBEF3C072C3BBEF80003477D /* XCRemoteSwiftPackageReference "swift-log" */; productName = Logging; }; /* End XCSwiftPackageProductDependency section */ }; rootObject = EBEF3A162C3B255A0003477D /* Project object */; } ================================================ FILE: approf.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: approf.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: approf.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved ================================================ { "originHash" : "bca11b0e59deddefb76aef5d49927075ef9e4a1238494b5a9858456b4fffe32b", "pins" : [ { "identity" : "combine-schedulers", "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/combine-schedulers", "state" : { "revision" : "9fa31f4403da54855f1e2aeaeff478f4f0e40b13", "version" : "1.0.2" } }, { "identity" : "keyboardshortcuts", "kind" : "remoteSourceControl", "location" : "https://github.com/sindresorhus/KeyboardShortcuts", "state" : { "revision" : "2e5f15581fefb821d4b366e57d817be8bf12aa58", "version" : "2.0.1" } }, { "identity" : "popupview", "kind" : "remoteSourceControl", "location" : "https://github.com/exyte/PopupView.git", "state" : { "revision" : "21119b8dbb413e03b6036b788601e96806c83ed3", "version" : "3.0.4" } }, { "identity" : "pow", "kind" : "remoteSourceControl", "location" : "https://github.com/EmergeTools/Pow.git", "state" : { "revision" : "f0d0f3e72d42beaf2b01f1cb798e1b55902814eb", "version" : "1.0.4" } }, { "identity" : "swift-case-paths", "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-case-paths", "state" : { "revision" : "642e6aab8e03e5f992d9c83e38c5be98cfad5078", "version" : "1.5.5" } }, { "identity" : "swift-clocks", "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-clocks", "state" : { "revision" : "b9b24b69e2adda099a1fa381cda1eeec272d5b53", "version" : "1.0.5" } }, { "identity" : "swift-collections", "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-collections", "state" : { "revision" : "3d2dc41a01f9e49d84f0a3925fb858bed64f702d", "version" : "1.1.2" } }, { "identity" : "swift-composable-architecture", "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-composable-architecture", "state" : { "revision" : "0d8980f5bcc5fe6941f1788758667ff2b8c10f03", "version" : "1.14.0" } }, { "identity" : "swift-concurrency-extras", "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-concurrency-extras", "state" : { "revision" : "bb5059bde9022d69ac516803f4f227d8ac967f71", "version" : "1.1.0" } }, { "identity" : "swift-custom-dump", "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-custom-dump", "state" : { "revision" : "82645ec760917961cfa08c9c0c7104a57a0fa4b1", "version" : "1.3.3" } }, { "identity" : "swift-dependencies", "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-dependencies", "state" : { "revision" : "3ef38bb702a1a2f39c7e19fc0578403b8ee52b17", "version" : "1.3.9" } }, { "identity" : "swift-identified-collections", "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-identified-collections", "state" : { "revision" : "2f5ab6e091dd032b63dacbda052405756010dc3b", "version" : "1.1.0" } }, { "identity" : "swift-log", "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-log", "state" : { "revision" : "9cb486020ebf03bfa5b5df985387a14a98744537", "version" : "1.6.1" } }, { "identity" : "swift-navigation", "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-navigation", "state" : { "revision" : "e834b3760731160d7d448509ee6a1408c8582a6b", "version" : "2.2.0" } }, { "identity" : "swift-perception", "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-perception", "state" : { "revision" : "bc67aa8e461351c97282c2419153757a446ae1c9", "version" : "1.3.5" } }, { "identity" : "swift-syntax", "kind" : "remoteSourceControl", "location" : "https://github.com/swiftlang/swift-syntax", "state" : { "revision" : "4c6cc0a3b9e8f14b3ae2307c5ccae4de6167ac2c", "version" : "600.0.0-prerelease-2024-06-12" } }, { "identity" : "swiftui-introspect", "kind" : "remoteSourceControl", "location" : "https://github.com/siteline/swiftui-introspect", "state" : { "revision" : "807f73ce09a9b9723f12385e592b4e0aaebd3336", "version" : "1.3.0" } }, { "identity" : "xctest-dynamic-overlay", "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", "state" : { "revision" : "bc2a151366f2cd0e347274544933bc2acb00c9fe", "version" : "1.4.0" } } ], "version" : 3 } ================================================ FILE: approf.xcodeproj/xcshareddata/xcschemes/approf.xcscheme ================================================ ================================================ FILE: approfTests/Files/Dummy.swift ================================================ //Created for approf in 2024 import Foundation class Dummy{ } ================================================ FILE: approfTests/TestAppDropFiles.swift ================================================ // Created for approf in 2024 import ComposableArchitecture import Foundation import Testing @testable import approf struct TestDropFiles2 { @Test @MainActor func testAppDropFiles() async throws { let urlA = Bundle(for: Dummy.self).url(forResource: "a.pb", withExtension: "gz")! let urlB = Bundle(for: Dummy.self).url(forResource: "b", withExtension: "pb")! let clock = TestClock() let dg = DateGenerator.constant(.distantPast) let store = TestStore(initialState: AppFeature.State()) { AppFeature() } withDependencies: { $0.uuid = .incrementing $0.continuousClock = clock $0.date = dg } await store.send(.onAppear) { $0.synced = true $0.pprofsSelectedId = nil } let sharedBasic = PProfBasic(uuid: UUID(0), filePaths: [urlA.path(percentEncoded: false), urlB.path(percentEncoded: false)], createdAt: dg.now, presentation: .diff) await store.send(.drop(.onDropEnds([urlA, urlB]))) { $0.drop.destination = .uth(UnderTheHood.State(basic: Shared(sharedBasic))) } await store.send(\.drop.destination.uth.delegate.onConfirmImportButtonTapped) { $0.drop.destination = nil } await clock.advance(by: .seconds(1)) var newBasic = sharedBasic newBasic.id = UUID(1) await store.receive(\.drop.delegate.addNewBasic){ $0.basics = [newBasic] $0.pprofs = [DetailFeature.State(basic: Shared(newBasic), period: .idle(IdleFeature.State()))] } await store.receive(\.onPprofsSelectedIdChanged){ $0.pprofsSelectedId = newBasic.id } } } ================================================ FILE: approfTests/TestDropFiles.swift ================================================ // Created for approf in 2024 import ComposableArchitecture import Testing import Foundation @testable import approf struct TestDropFiles { @Test @MainActor func testDropFiles() async throws { let urlA = Bundle(for: Dummy.self).url(forResource: "a.pb", withExtension: "gz")! let urlB = Bundle(for: Dummy.self).url(forResource: "b", withExtension: "pb")! let clock = TestClock() let dg = DateGenerator.constant(.distantPast) let store = TestStore(initialState: AppFeature.State()) { AppFeature() } withDependencies: { $0.uuid = .incrementing $0.continuousClock = clock $0.date = dg } let dropStore = TestStore(initialState: DropAndImportFeature.State()) { DropAndImportFeature() } withDependencies: { $0.uuid = .incrementing $0.continuousClock = clock $0.date = dg } await store.send(.onAppear) { $0.synced = true } let sharedBasic = PProfBasic(uuid: UUID(0), filePaths: [urlA.path(percentEncoded: false), urlB.path(percentEncoded: false)], createdAt: dg.now, presentation: .diff) await clock.advance(by: .seconds(1)) await dropStore.send(.onDropEnds([urlA, urlB])) { $0.destination = .uth(UnderTheHood.State(basic: Shared(sharedBasic))) } await store.send(.drop(.onDropEnds([urlA, urlB]))) { $0.drop.destination = .uth(UnderTheHood.State(basic: Shared(sharedBasic))) } } } ================================================ FILE: approfTests/TestDropFilesLegacy.swift ================================================ // Created for approf in 2024 import ComposableArchitecture import XCTest @testable import approf final class TestDropFilesLegacy: XCTestCase { @MainActor func testDropFiles() async throws { let urlA = Bundle(for: Dummy.self).url(forResource: "a.pb", withExtension: "gz")! let urlB = Bundle(for: Dummy.self).url(forResource: "b", withExtension: "pb")! let clock = TestClock() let dg = DateGenerator.constant(.distantPast) let store = TestStore(initialState: AppFeature.State()) { AppFeature() } withDependencies: { $0.uuid = .incrementing $0.continuousClock = clock $0.date = dg } let dropStore = TestStore(initialState: DropAndImportFeature.State()) { DropAndImportFeature() } withDependencies: { $0.uuid = .incrementing $0.continuousClock = clock $0.date = dg } await store.send(.onAppear) { $0.synced = true } let sharedBasic = PProfBasic(uuid: UUID(0), filePaths: [urlA.path(percentEncoded: false), urlB.path(percentEncoded: false)], createdAt: dg.now, presentation: .diff) await clock.advance(by: .seconds(1)) await dropStore.send(.onDropEnds([urlA, urlB])) { $0.destination = .uth(UnderTheHood.State(basic: Shared(sharedBasic))) } await store.send(.drop(.onDropEnds([urlA, urlB]))) { $0.drop.destination = .uth(UnderTheHood.State(basic: Shared(sharedBasic))) } } } ================================================ FILE: approfTests/TestPlan.xctestplan ================================================ { "configurations" : [ { "id" : "FD72B178-E2F5-4F38-9F74-ED8079928207", "name" : "Configuration 1", "options" : { } } ], "defaultOptions" : { "testTimeoutsEnabled" : true }, "testTargets" : [ { "target" : { "containerPath" : "container:approf.xcodeproj", "identifier" : "EBEF3A2E2C3B255B0003477D", "name" : "approfTests" } } ], "version" : 1 } ================================================ FILE: approfUITests/pprofUITests.swift ================================================ // // pprofUITests.swift // pprofUITests // // Created by Clement on 08/07/2024. // import XCTest final class pprofUITests: XCTestCase { override func setUpWithError() throws { // Put setup code here. This method is called before the invocation of each test method in the class. // In UI tests it is usually best to stop immediately when a failure occurs. continueAfterFailure = false // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. } override func tearDownWithError() throws { // Put teardown code here. This method is called after the invocation of each test method in the class. } @MainActor func testExample() throws { // UI tests must launch the application that they test. let app = XCUIApplication() app.launch() // Use XCTAssert and related functions to verify your tests produce the correct results. } @MainActor func testLaunchPerformance() throws { if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { // This measures how long it takes to launch your application. measure(metrics: [XCTApplicationLaunchMetric()]) { XCUIApplication().launch() } } } } ================================================ FILE: approfUITests/pprofUITestsLaunchTests.swift ================================================ // // pprofUITestsLaunchTests.swift // pprofUITests // // Created by Clement on 08/07/2024. // import XCTest final class pprofUITestsLaunchTests: XCTestCase { override class var runsForEachTargetApplicationUIConfiguration: Bool { true } override func setUpWithError() throws { continueAfterFailure = false } @MainActor func testLaunch() throws { let app = XCUIApplication() app.launch() // Insert steps here to perform after app launch but before taking a screenshot, // such as logging into a test account or navigating somewhere in the app let attachment = XCTAttachment(screenshot: app.screenshot()) attachment.name = "Launch Screen" attachment.lifetime = .keepAlways add(attachment) } } ================================================ FILE: ci_scripts/ci_post_clone.sh ================================================ #!/bin/bash #defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES mkdir -p ~/Library/org.swift.swiftpm/security/ cp macros.json ~/Library/org.swift.swiftpm/security/ ================================================ FILE: ci_scripts/macros.json ================================================ [ { "fingerprint" : "031704ba0634b45e02fe875b8ddddc7f30a07f49", "packageIdentity" : "swift-case-paths", "targetName" : "CasePathsMacros" }, { "fingerprint" : "d65f3c1b9332255a40002905b6787f765585d7fb", "packageIdentity" : "swift-composable-architecture", "targetName" : "ComposableArchitectureMacros" }, { "fingerprint" : "52018827ce21e482a36e3795bea2666b3898164c", "packageIdentity" : "swift-dependencies", "targetName" : "DependenciesMacrosPlugin" }, { "fingerprint" : "2c75ce556a6fc106721b0dadc2c7327244ad3999", "packageIdentity" : "swift-perception", "targetName" : "PerceptionMacros" } ]