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
Command line under the hood
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
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: "")
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: "")
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"
}
]