Full Code of dena-sohrabi/There for AI

main 61863ffb560c cached
60 files
111.5 KB
27.5k tokens
1 requests
Download .txt
Repository: dena-sohrabi/There
Branch: main
Commit: 61863ffb560c
Files: 60
Total size: 111.5 KB

Directory structure:
gitextract_iq1mvdbh/

├── .gitignore
├── LICENSE
├── There/
│   ├── Assets.xcassets/
│   │   ├── AccentColor.colorset/
│   │   │   └── Contents.json
│   │   ├── AppIcon.appiconset/
│   │   │   └── Contents.json
│   │   ├── Contents.json
│   │   ├── Earth.imageset/
│   │   │   └── Contents.json
│   │   ├── Logo.imageset/
│   │   │   └── Contents.json
│   │   ├── Night.imageset/
│   │   │   └── Contents.json
│   │   ├── appIcon.imageset/
│   │   │   └── Contents.json
│   │   ├── early-afternoon.imageset/
│   │   │   └── Contents.json
│   │   ├── early-evening.imageset/
│   │   │   └── Contents.json
│   │   ├── early-morning.imageset/
│   │   │   └── Contents.json
│   │   ├── evening.imageset/
│   │   │   └── Contents.json
│   │   ├── late-afternoon.imageset/
│   │   │   └── Contents.json
│   │   ├── late-morning.imageset/
│   │   │   └── Contents.json
│   │   ├── telegram-logo.imageset/
│   │   │   └── Contents.json
│   │   └── twitter.imageset/
│   │       └── Contents.json
│   ├── ContentView.swift
│   ├── Data/
│   │   ├── Database.swift
│   │   ├── Entry.swift
│   │   ├── Fetcher.swift
│   │   ├── LaunchAgent.swift
│   │   ├── Persistence.swift
│   │   ├── Router.swift
│   │   ├── SearchCompleter.swift
│   │   └── Utilities.swift
│   ├── Preview Content/
│   │   └── Preview Assets.xcassets/
│   │       └── Contents.json
│   ├── There.entitlements
│   ├── ThereApp.swift
│   ├── UI/
│   │   ├── Button.swift
│   │   ├── Heading.swift
│   │   ├── Input.swift
│   │   ├── Label.swift
│   │   ├── LocalImageView.swift
│   │   ├── Titlebar.swift
│   │   └── TransparentBackgroundView.swift
│   ├── Views/
│   │   ├── ButtomBar/
│   │   │   ├── AddButton.swift
│   │   │   ├── BottomBarView.swift
│   │   │   └── SettingsButton.swift
│   │   ├── CLAuthorizationStatus+Description.swift 
│   │   ├── EmptyTimezoneView.swift
│   │   ├── EntryUI/
│   │   │   ├── EntryIcon.swift
│   │   │   └── EntryRow.swift
│   │   ├── MainView.swift
│   │   ├── Onboarding/
│   │   │   ├── InitialView.swift
│   │   │   ├── LeftPanel.swift
│   │   │   └── RightPanel.swift
│   │   └── Timezone/
│   │       ├── AddTimezone + Components.swift
│   │       ├── AddTimezone + Functions.swift
│   │       ├── AddTimezone.swift
│   │       ├── EditTimeZone + Functions.swift
│   │       ├── EditTimeZoneView.swift
│   │       ├── FormSection.swift
│   │       ├── IconSection.swift
│   │       └── NotFoundView.swift
│   └── pm.there.There.LaunchAgent.plist
├── ThereTests/
│   └── ThereTests.swift
├── ThereUITests/
│   ├── ThereUITests.swift
│   └── ThereUITestsLaunchTests.swift
└── readme.md

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Xcode
.DS_Store
xcuserdata/
*.xcodeproj/*
!*.xcodeproj/project.pbxproj
!*.xcodeproj/xcshareddata/
!*.xcodeproj/project.xcworkspace/
!*.xcworkspace/contents.xcworkspacedata
/*.gcno
**/xcshareddata/WorkspaceSettings.xcsettings

# Swift Package Manager
.build/
Packages/
Package.pins
Package.resolved
*.xcodeproj

# CocoaPods
Pods/

# Carthage
Carthage/Build/

# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output

# Code Injection
injectionforxcode/

# User-specific files
*.swp
*~
.vscode/
*.moved-aside

# macOS specific
.AppleDouble
.LSOverride
Icon
._*
.Spotlight-V100
.Trashes

# Build products
build/
DerivedData/
*.hmap
*.ipa
*.dSYM.zip
*.dSYM

# Playgrounds
timeline.xctimeline
playground.xcworkspace

# Swift Package Manager
.swiftpm

# App packaging
*.app
*.pkg
*.dmg

# Crash logs
*.crash
*.ips

# Other
*.log
*.sqlite


================================================
FILE: LICENSE
================================================
### MIT License

```
MIT License

Copyright (c) 2024 Dena Sohrabi

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:

1. The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

2. 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: There/Assets.xcassets/AccentColor.colorset/Contents.json
================================================
{
  "colors" : [
    {
      "idiom" : "universal"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: There/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "icon-166.png",
      "idiom" : "mac",
      "scale" : "1x",
      "size" : "16x16"
    },
    {
      "filename" : "icon-1662.png",
      "idiom" : "mac",
      "scale" : "2x",
      "size" : "16x16"
    },
    {
      "filename" : "icon-3222222.png",
      "idiom" : "mac",
      "scale" : "1x",
      "size" : "32x32"
    },
    {
      "filename" : "icon-322e22ee2.png",
      "idiom" : "mac",
      "scale" : "2x",
      "size" : "32x32"
    },
    {
      "filename" : "e1e11221ee21.png",
      "idiom" : "mac",
      "scale" : "1x",
      "size" : "128x128"
    },
    {
      "filename" : "icon-256feefef.png",
      "idiom" : "mac",
      "scale" : "2x",
      "size" : "128x128"
    },
    {
      "filename" : "ddwdwdwdw 2.png",
      "idiom" : "mac",
      "scale" : "1x",
      "size" : "256x256"
    },
    {
      "filename" : "dwqqdwdwqdqwdqw.png",
      "idiom" : "mac",
      "scale" : "2x",
      "size" : "256x256"
    },
    {
      "filename" : "dwqqdwdwqdqwdqw 1.png",
      "idiom" : "mac",
      "scale" : "1x",
      "size" : "512x512"
    },
    {
      "filename" : "dqdwqdwqdwq.png",
      "idiom" : "mac",
      "scale" : "2x",
      "size" : "512x512"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: There/Assets.xcassets/Contents.json
================================================
{
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: There/Assets.xcassets/Earth.imageset/Contents.json
================================================
{
  "images" : [
    {
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "Earch&Sun.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: There/Assets.xcassets/Logo.imageset/Contents.json
================================================
{
  "images" : [
    {
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "ThereIcon.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: There/Assets.xcassets/Night.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "Night.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: There/Assets.xcassets/appIcon.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "iconTemplate.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "iconTemplate@2x.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "iconTemplate@3x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  },
  "properties" : {
    "template-rendering-intent" : "template"
  }
}


================================================
FILE: There/Assets.xcassets/early-afternoon.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "Early afternoon.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: There/Assets.xcassets/early-evening.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "Early Eavning.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: There/Assets.xcassets/early-morning.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "Early Morning.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: There/Assets.xcassets/evening.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "Eavning.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: There/Assets.xcassets/late-afternoon.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "Late Afternoon.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: There/Assets.xcassets/late-morning.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "Late Morning.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: There/Assets.xcassets/telegram-logo.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "Logo.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: There/Assets.xcassets/twitter.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "twitter.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: There/ContentView.swift
================================================
import SwiftUI

struct ContentView: View {
    @EnvironmentObject var router: Router
    var body: some View {
        switch router.activeRoute {
        case .mainView:
            MainView()
        case .addTimezone:
            AddTimezone()
                .transition(.asymmetric(insertion: .push(from: .trailing), removal: .push(from: .leading)))
        case let .editTimeZone(entryId):
            EditTimeZoneView(entryId: entryId)
                .transition(.asymmetric(insertion: .push(from: .trailing), removal: .push(from: .leading)))
        }
    }
}

#Preview {
    ContentView()
        .frame(width: 300, height: 400)
}


================================================
FILE: There/Data/Database.swift
================================================
import Foundation
import GRDB
import os.log

///
/// You create an `AppDatabase` with a connection to an SQLite database
/// (see <https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/databaseconnections>).
///
/// Create those connections with a configuration returned from
/// `AppDatabase/makeConfiguration(_:)`.
///
/// For example:
///
/// ```swift
/// // Create an in-memory AppDatabase
/// let config = AppDatabase.makeConfiguration()
/// let dbQueue = try DatabaseQueue(configuration: config)
/// let appDatabase = try AppDatabase(dbQueue)
/// ```
struct AppDatabase {
    /// Creates an `AppDatabase`, and makes sure the database schema
    /// is ready.
    ///
    /// - important: Create the `DatabaseWriter` with a configuration
    ///   returned by ``makeConfiguration(_:)``.
    init(_ dbWriter: any DatabaseWriter) throws {
        self.dbWriter = dbWriter
        try migrator.migrate(dbWriter)
    }

    /// Provides access to the database.
    ///
    /// Application can use a `DatabasePool`, while SwiftUI previews and tests
    /// can use a fast in-memory `DatabaseQueue`.
    ///
    /// See <https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/databaseconnections>
    let dbWriter: any DatabaseWriter
}

// MARK: - Database Configuration

extension AppDatabase {
    private static let sqlLogger = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "SQL")

    /// Returns a database configuration suited for `PlayerRepository`.
    ///
    /// SQL statements are logged if the `SQL_TRACE` environment variable
    /// is set.
    ///
    /// - parameter base: A base configuration.
    public static func makeConfiguration(_ base: Configuration = Configuration()) -> Configuration {
        var config = base

        // An opportunity to add required custom SQL functions or
        // collations, if needed:
        // config.prepareDatabase { db in
        //     db.add(function: ...)
        // }

        // Log SQL statements if the `SQL_TRACE` environment variable is set.
        // See <https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/database/trace(options:_:)>
        if ProcessInfo.processInfo.environment["SQL_TRACE"] != nil {
            config.prepareDatabase { db in
                db.trace {
                    // It's ok to log statements publicly. Sensitive
                    // information (statement arguments) are not logged
                    // unless config.publicStatementArguments is set
                    // (see below).
                    os_log("%{public}@", log: sqlLogger, type: .debug, String(describing: $0))
                }
            }
        }

        #if DEBUG
            // Protect sensitive information by enabling verbose debugging in
            // DEBUG builds only.
            // See <https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/configuration/publicstatementarguments>
            config.publicStatementArguments = true
        #endif

        return config
    }
}

// MARK: - Database Migrations

extension AppDatabase {
    /// The DatabaseMigrator that defines the database schema.
    ///
    /// See <https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/migrations>
    private var migrator: DatabaseMigrator {
        var migrator = DatabaseMigrator()

        #if DEBUG
            // Speed up development by nuking the database when migrations change
            // See <https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/migrations>
            migrator.eraseDatabaseOnSchemaChange = true
        #endif

        // Migrations for future application versions will be inserted here:
        migrator.registerMigration("add entry") { db in
            try db.create(table: Entry.databaseTableName) { t in
                t.primaryKey("id", .integer).notNull()
                t.column(Entry.Columns.type.rawValue, .text).notNull()
                t.column(Entry.Columns.name.rawValue, .text).notNull()
                t.column(Entry.Columns.city.rawValue, .text).notNull()
                t.column(Entry.Columns.timezoneIdentifier.rawValue, .text).notNull()
                t.column(Entry.Columns.flag.rawValue, .text)
                t.column(Entry.Columns.photoData.rawValue, .text)
            }
        }

        return migrator
    }
}

// MARK: - Database Access: Writes

// The write methods execute invariant-preserving database transactions.

extension AppDatabase {
    /// A validation error that prevents some players from being saved into
    /// the database.
    enum ValidationError: LocalizedError {
        case missingName

        var errorDescription: String? {
            switch self {
            case .missingName:
                return "Please provide a name"
            }
        }
    }
}

// MARK: - Database Access: Reads

// This demo app does not provide any specific reading method, and instead
// gives an unrestricted read-only access to the rest of the application.
// In your app, you are free to choose another path, and define focused
// reading methods.
extension AppDatabase {
    /// Provides a read-only access to the database
    var reader: DatabaseReader {
        dbWriter
    }
}


================================================
FILE: There/Data/Entry.swift
================================================
import Foundation
import GRDB
import SwiftUI

enum EntryType: String, Codable {
    case place
    case person
}

enum DayPeriod: String, CaseIterable {
    case earlyMorning = "early-morning"
    case lateMorning = "late-morning"
    case earlyAfternoon = "early-afternoon"
    case lateAfternoon = "late-afternoon"
    case earlyEvening = "early-evening"
    case evening
    case night
}

struct Entry: Codable, Equatable, Identifiable, FetchableRecord, PersistableRecord {
    let id: Int64
    var type: EntryType
    var name: String
    var city: String
    var timezoneIdentifier: String
    var flag: String?
    var photoData: String?

    enum Columns: String, ColumnExpression {
        case id, type, name, city, country, timezoneIdentifier, flag, photoData
    }

    init(id: Int64 = Int64.random(in: 1 ... 99999), type: EntryType, name: String, city: String, timezoneIdentifier: String, flag: String? = nil, photoData: String? = nil) {
        self.id = id
        self.type = type
        self.name = name
        self.city = city
        self.timezoneIdentifier = timezoneIdentifier
        self.flag = flag
        self.photoData = photoData
    }

    // MARK: - Codable

    enum CodingKeys: String, CodingKey {
        case id, type, name, city, timezoneIdentifier, flag, photoData
    }

    var timeDifference: (hours: Int, minutes: Int, dayPeriod: DayPeriod) {
        let currentDate = Date()
        let calendar = Calendar.current

        // Get the current user's time zone
        let localTimeZone = TimeZone.current

        // Get the entry's time zone
        guard let entryTimeZone = TimeZone(identifier: timezoneIdentifier) else {
            return (0, 0, .night)
        }

        // Calculate the time difference
        let differenceInSeconds = entryTimeZone.secondsFromGMT(for: currentDate) - localTimeZone.secondsFromGMT(for: currentDate)

        // Convert seconds to hours and minutes
        let hours = differenceInSeconds / 3600
        let minutes = (differenceInSeconds % 3600) / 60

        // Calculate the time in the entry's time zone
        var entryDateComponents = calendar.dateComponents(in: entryTimeZone, from: currentDate)
        let entryHour = entryDateComponents.hour ?? 0

        let dayPeriod: DayPeriod
        switch entryHour {
        case 5 ..< 8:
            dayPeriod = .earlyMorning
        case 8 ..< 12:
            dayPeriod = .lateMorning
        case 12 ..< 15:
            dayPeriod = .earlyAfternoon
        case 15 ..< 17:
            dayPeriod = .lateAfternoon
        case 17 ..< 19:
            dayPeriod = .earlyEvening
        case 19 ..< 22:
            dayPeriod = .evening
        default:
            dayPeriod = .night
        }

        return (hours, minutes, dayPeriod)
    }

    var timeIcon: String {
        return timeDifference.dayPeriod.rawValue
    }
}


================================================
FILE: There/Data/Fetcher.swift
================================================
import Combine
import Foundation
import GRDB

enum SortOrder {
    case timeAscending
    case timeDescending
}

class Fetcher: ObservableObject {
    @Published var entries: [Entry] = []
    @Published var getEntries: AnyCancellable?
    var database: AppDatabase = .shared

    init() {
        getEntries = ValueObservation.tracking { db in
            try Entry.fetchAll(db)
        }
        .publisher(in: database.dbWriter, scheduling: .immediate)
        .sink(
            receiveCompletion: { _ in /* ignore error */ },
            receiveValue: { [weak self] entries in
                self?.entries = entries
            }
        )
    }

    func sortEntries(by order: SortOrder) {
        switch order {
        case .timeAscending:
            entries.sort { $0.timeDifference.hours < $1.timeDifference.hours }
        case .timeDescending:
            entries.sort { $0.timeDifference.hours > $1.timeDifference.hours }
        }
    }
}


================================================
FILE: There/Data/LaunchAgent.swift
================================================
import Foundation
import ServiceManagement

func installLaunchAgent() {
    let fileManager = FileManager.default

    guard let libraryDirectory = fileManager.urls(for: .libraryDirectory, in: .userDomainMask).first else {
        print("Unable to find user's Library directory")
        return
    }

    let launchAgentsDirectory = libraryDirectory.appendingPathComponent("LaunchAgents")
    let plistName = "pm.there.There.LaunchAgent.plist"
    let plistPath = launchAgentsDirectory.appendingPathComponent(plistName)

    // Create LaunchAgents directory if it doesn't exist
    if !fileManager.fileExists(atPath: launchAgentsDirectory.path) {
        do {
            try fileManager.createDirectory(at: launchAgentsDirectory, withIntermediateDirectories: true, attributes: nil)
        } catch {
            print("Error creating LaunchAgents directory: \(error)")
            return
        }
    }

    // Create the plist content
    let plistContent = """
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>Label</key>
        <string>pm.there.There.LaunchAgent</string>
        <key>ProgramArguments</key>
        <array>
            <string>/Applications/There.app/Contents/MacOS/There</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
        <key>KeepAlive</key>
        <true/>
        <key>LimitLoadToSessionType</key>
        <string>Aqua</string>
    </dict>
    </plist>
    """

    do {
        // Write the plist content to the file
        try plistContent.write(to: plistPath, atomically: true, encoding: .utf8)
        print("Launch Agent plist created successfully")

        // Set the correct permissions
        try fileManager.setAttributes([.posixPermissions: 0o644], ofItemAtPath: plistPath.path)

        // Load the launch agent
        try Process.run(URL(fileURLWithPath: "/bin/launchctl"), arguments: ["load", plistPath.path])
        print("Launch Agent loaded successfully")
    } catch {
        print("Error installing or loading Launch Agent: \(error)")
    }

    // For macOS 13 and later, also register using SMAppService
    if #available(macOS 13.0, *) {
        do {
            try SMAppService.mainApp.register()
            print("App registered as login item using SMAppService")
        } catch {
            print("Failed to register app as login item using SMAppService: \(error)")
        }
    }
}

func uninstallLaunchAgent() {
    let fileManager = FileManager.default
    guard let libraryDirectory = fileManager.urls(for: .libraryDirectory, in: .userDomainMask).first else {
        print("Unable to find user's Library directory")
        return
    }

    let launchAgentsDirectory = libraryDirectory.appendingPathComponent("LaunchAgents")
    let plistName = "pm.there.There.LaunchAgent.plist"
    let plistPath = launchAgentsDirectory.appendingPathComponent(plistName)

    do {
        // Unload the launch agent
        try Process.run(URL(fileURLWithPath: "/bin/launchctl"), arguments: ["unload", plistPath.path])
        print("Launch Agent unloaded successfully")

        // Remove the plist file
        try fileManager.removeItem(at: plistPath)
        print("Launch Agent plist removed successfully")
    } catch {
        print("Error uninstalling Launch Agent: \(error)")
    }

    // For macOS 13 and later, also unregister using SMAppService
    if #available(macOS 13.0, *) {
        do {
            try SMAppService.mainApp.unregister()
            print("App unregistered as login item using SMAppService")
        } catch {
            print("Failed to unregister app as login item using SMAppService: \(error)")
        }
    }
}


================================================
FILE: There/Data/Persistence.swift
================================================
import Foundation
import GRDB

extension AppDatabase {
    /// The database for the application
    static let shared = makeShared()

    private static func makeShared() -> AppDatabase {
        do {
            // Apply recommendations from
            // <https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/databaseconnections>
            //
            // Create the "Application Support/Database" directory if needed
            let fileManager = FileManager.default
            let appSupportURL = try fileManager.url(
                for: .applicationSupportDirectory, in: .userDomainMask,
                appropriateFor: nil, create: true)
            let directoryURL = appSupportURL.appendingPathComponent("Database", isDirectory: true)

            // Support for tests: delete the database if requested
            if CommandLine.arguments.contains("-reset") {
                try? fileManager.removeItem(at: directoryURL)
            }

            // Create the database folder if needed
            try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true)

            // Open or create the database
            let databaseURL = directoryURL.appendingPathComponent("db.sqlite")
            NSLog("Database stored at \(databaseURL.path)")
            let dbPool = try DatabasePool(
                path: databaseURL.path,
                // Use default AppDatabase configuration
                configuration: AppDatabase.makeConfiguration())

            // Create the AppDatabase
            let appDatabase = try AppDatabase(dbPool)

            return appDatabase
        } catch {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate.
            //
            // Typical reasons for an error here include:
            // * The parent directory cannot be created, or disallows writing.
            // * The database is not accessible, due to permissions or data protection when the device is locked.
            // * The device is out of space.
            // * The database could not be migrated to its latest schema version.
            // Check the error message to determine what the actual problem was.
            fatalError("Unresolved error \(error)")
        }
    }

    /// Creates an empty database for SwiftUI previews
    static func empty() -> AppDatabase {
        // Connect to an in-memory database
        // See https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/databaseconnections
        let dbQueue = try! DatabaseQueue(configuration: AppDatabase.makeConfiguration())
        return try! AppDatabase(dbQueue)
    }

    /// Creates a database full of random players for SwiftUI previews
    static func random() -> AppDatabase {
        let appDatabase = empty()
        return appDatabase
    }
}


================================================
FILE: There/Data/Router.swift
================================================
import SwiftUI

enum Route {
    case addTimezone
    case mainView
    case editTimeZone(entryId: Int64?)
}

class Router: ObservableObject {
    @Published var activeRoute: Route = .mainView

    func setActiveRoute(to route: Route) {
        withAnimation(.default) {
            activeRoute = route
        }
    }

    func cleanActiveRoute() {
        withAnimation(.default) {
            activeRoute = .mainView
        }
    }
}


================================================
FILE: There/Data/SearchCompleter.swift
================================================
import Foundation
import MapKit

class TimeZoneSearchCompiler: NSObject {
    private var commonAbbreviations: [String: (fullName: String, identifier: String)]
    private var utcOffsets: [String: String]
    private var currentCompletion: (([TimeZoneSearchResult]) -> Void)?

    override init() {
        // Initialize common abbreviations
        commonAbbreviations = [
            // North America
            "EST": ("Eastern Standard Time", "America/New_York"),
            "EDT": ("Eastern Daylight Time", "America/New_York"),
            "CST": ("Central Standard Time", "America/Chicago"),
            "CDT": ("Central Daylight Time", "America/Chicago"),
            "MST": ("Mountain Standard Time", "America/Denver"),
            "MDT": ("Mountain Daylight Time", "America/Denver"),
            "PST": ("Pacific Standard Time", "America/Los_Angeles"),
            "PDT": ("Pacific Daylight Time", "America/Los_Angeles"),
            "AKST": ("Alaska Standard Time", "America/Anchorage"),
            "AKDT": ("Alaska Daylight Time", "America/Anchorage"),
            "HST": ("Hawaii Standard Time", "Pacific/Honolulu"),

            // Europe
            "GMT": ("Greenwich Mean Time", "Etc/GMT"),
            "BST": ("British Summer Time", "Europe/London"),
            "CET": ("Central European Time", "Europe/Paris"),
            "CEST": ("Central European Summer Time", "Europe/Paris"),

            // Asia
            "IST": ("India Standard Time", "Asia/Kolkata"),
            "JST": ("Japan Standard Time", "Asia/Tokyo"),

            // Australia
            "AEST": ("Australian Eastern Standard Time", "Australia/Sydney"),
            "AEDT": ("Australian Eastern Daylight Time", "Australia/Sydney"),

            // Coordinated Universal Time
            "UTC": ("Coordinated Universal Time", "Etc/UTC"),
        ]

        // Initialize UTC offsets
        utcOffsets = [
            "UTC+0": "Etc/GMT",
            "UTC-1": "Etc/GMT+1",
            "UTC-2": "Etc/GMT+2",
            "UTC-3": "Etc/GMT+3",
            "UTC-4": "Etc/GMT+4",
            "UTC-5": "Etc/GMT+5",
            "UTC-6": "Etc/GMT+6",
            "UTC-7": "Etc/GMT+7",
            "UTC-8": "Etc/GMT+8",
            "UTC-9": "Etc/GMT+9",
            "UTC-10": "Etc/GMT+10",
            "UTC-11": "Etc/GMT+11",
            "UTC-12": "Etc/GMT+12",
            "UTC+1": "Etc/GMT-1",
            "UTC+2": "Etc/GMT-2",
            "UTC+3": "Etc/GMT-3",
            "UTC+4": "Etc/GMT-4",
            "UTC+5": "Etc/GMT-5",
            "UTC+5:30": "Asia/Kolkata",
            "UTC+6": "Etc/GMT-6",
            "UTC+7": "Etc/GMT-7",
            "UTC+8": "Etc/GMT-8",
            "UTC+9": "Etc/GMT-9",
            "UTC+10": "Etc/GMT-10",
            "UTC+11": "Etc/GMT-11",
            "UTC+12": "Etc/GMT-12",
            "UTC+13": "Pacific/Apia",
            "UTC+14": "Pacific/Kiritimati",
        ]

        super.init()
    }

    func search(query: String, completion: @escaping ([TimeZoneSearchResult]) -> Void) {
        currentCompletion = completion

        let results = searchAbbreviations(query: query) + searchUTCOffsets(query: query)

        if !results.isEmpty {
            completion(results)
            return
        }

        let request = MKLocalSearch.Request()
        request.naturalLanguageQuery = query
        request.resultTypes = .address
        

        let search = MKLocalSearch(request: request)
        
        search.start { [weak self] response, _ in
            guard let self = self, let response = response else {
                completion([])
                return
            }

            let results = self.processResults(response.mapItems)
            DispatchQueue.main.async {
                completion(results)
            }
        }
    }

    private func processResults(_ mapItems: [MKMapItem]) -> [TimeZoneSearchResult] {
        return mapItems.compactMap { item in
//            guard let placemark = item.placemark else { return nil }
            let placemark = item.placemark
            

            let city = placemark.locality ?? placemark.name ?? ""
            let country = placemark.country ?? ""
            // placemark.administrativeArea,
            let subtitle = [country].compactMap { $0 }.joined(separator: ", ")

            print("placemark.timeZone \(city) \(placemark)")
            
            return TimeZoneSearchResult(
                title: city,
                subtitle: subtitle,
                identifier: placemark.timeZone?.identifier,
                type: .city,
                region: placemark.region,
                coordinate: placemark.location
            )
        }
    }

    private func searchAbbreviations(query: String) -> [TimeZoneSearchResult] {
        return commonAbbreviations
            .filter { $0.key.lowercased().contains(query.lowercased()) }
            .map { TimeZoneSearchResult(title: $0.key, subtitle: $0.value.fullName, identifier: $0.value.identifier, type: .abbreviation, region: nil, coordinate: nil) }
    }

    private func searchUTCOffsets(query: String) -> [TimeZoneSearchResult] {
        return utcOffsets
            .filter { $0.key.lowercased().contains(query.lowercased()) }
            .map { TimeZoneSearchResult(title: $0.key, subtitle: "Coordinated Universal Time Offset", identifier: $0.value, type: .utcOffset, region: nil, coordinate: nil) }
    }
}

struct TimeZoneSearchResult: Identifiable, Equatable {
    static func ==(lhs: TimeZoneSearchResult, rhs: TimeZoneSearchResult) -> Bool {
        lhs.id == rhs.id && lhs.title == rhs.title && lhs.subtitle == rhs.subtitle && lhs.identifier == rhs.identifier && lhs.type == rhs.type && lhs.region == rhs.region
    }
    
    let id = UUID()
    let title: String
    let subtitle: String
    let identifier: String?
    let type: TimeZoneSearchResultType
    let region: CLRegion?
    let coordinate: CLLocation?

    func getTimeZone() async  -> TimeZone? {
        switch type {
        case .city:
            print("title \(title) coordinate \(coordinate) region \(region)")
            if let coordinate = coordinate {
                
                return await TimeZone.timeZone(for: coordinate)
            }
            if let region = region as? CLCircularRegion {
                let location = CLLocation(latitude: region.center.latitude, longitude: region.center.longitude)
                return await TimeZone.timeZone(for: location)
            }
            return nil
        case .abbreviation, .utcOffset:
            return identifier.flatMap { TimeZone(identifier: $0) }
        }
    }
}

enum TimeZoneSearchResultType {
    case city
    case abbreviation
    case utcOffset
}

extension TimeZone {
    static func timeZone(for location: CLLocation) async -> TimeZone? {
           let geocoder = CLGeocoder()
           do {
               let placemarks = try await geocoder.reverseGeocodeLocation(location)
               print("\(placemarks)")
               return placemarks.first?.timeZone
           } catch {
               print("Geocoding error: \(error.localizedDescription)")
               return nil
           }
       }
}

class SearchCompleter: ObservableObject {
    @Published var results: [TimeZoneSearchResult] = []
    @Published var queryFragment: String = "" {
        didSet {
            if queryFragment.isEmpty {
                results = defaultResults
            } else {
                updateResults(for: queryFragment)
            }
        }
    }

    private let timeZoneSearchCompiler: TimeZoneSearchCompiler
    private var defaultResults: [TimeZoneSearchResult] = []

    init() {
        timeZoneSearchCompiler = TimeZoneSearchCompiler()
        setupDefaultResults()
    }

    private func setupDefaultResults() {
        // All timezones and locations
        for identifier in TimeZone.knownTimeZoneIdentifiers {
            let components = identifier.split(separator: "/")
            if components.count >= 2 {
                let location = String(components.last!).replacingOccurrences(of: "_", with: " ")
                let region = String(components.first!)
                defaultResults.append(TimeZoneSearchResult(title: location, subtitle: region, identifier: identifier, type: .city, region: nil, coordinate: nil))
            }
        }

        // Add UTC offsets
        for offset in -12 ... 14 {
            let sign = offset >= 0 ? "+" : ""
            let title = "UTC\(sign)\(offset)"
            let identifier = "Etc/GMT\(offset == 0 ? "" : (offset > 0 ? "-" : "+") + "\(abs(offset))")"
            defaultResults.append(TimeZoneSearchResult(title: title, subtitle: "Coordinated Universal Time Offset", identifier: identifier, type: .utcOffset, region: nil, coordinate: nil))
        }

        results = defaultResults
    }

    private var latestSearch = ""

    private func updateResults(for query: String) {
        latestSearch = query

        if query.isEmpty {
            results = defaultResults
        } else {
            timeZoneSearchCompiler.search(query: query) { [weak self] searchResults in
                DispatchQueue.main.async {
                    if self?.latestSearch == query {
                        self?.results = searchResults
                    } else {
                        // discard
                    }
                }
            }
        }
    }
}


================================================
FILE: There/Data/Utilities.swift
================================================
import Foundation
import SwiftUI

class Utils {
    public static var shared = Utils()
    func selectPhoto() -> NSImage? {
        let openPanel = NSOpenPanel()
        openPanel.allowedContentTypes = [.jpeg, .png]
        openPanel.allowsMultipleSelection = false
        openPanel.prompt = "Select Image"
        if openPanel.runModal() == .OK, let url = openPanel.url {
            return NSImage(contentsOf: url)
        } else {
            return nil
        }
    }

    /// Converts a two-letter country code to its corresponding emoji flag.
    ///
    /// - Parameter countryCode: ISO 3166-1 alpha-2 country code.
    /// - Returns: Emoji representation of the country's flag.
    func getCountryEmoji(for countryCode: String) -> String {
        // Unicode offset for Regional Indicator Symbols
        let base: UInt32 = 127397

        // Convert each letter to its corresponding Regional Indicator Symbol
        return countryCode.uppercased().unicodeScalars.map {
            String(UnicodeScalar(base + $0.value)!)
        }.joined()
    }
}


================================================
FILE: There/Preview Content/Preview Assets.xcassets/Contents.json
================================================
{
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: There/There.entitlements
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.developer.maps</key>
	<true/>
	<key>com.apple.security.app-sandbox</key>
	<true/>
	<key>com.apple.security.application-groups</key>
	<array>
		<string>group.pm.there.There</string>
	</array>
	<key>com.apple.security.files.user-selected.read-only</key>
	<true/>
	<key>com.apple.security.network.client</key>
	<true/>
	<key>com.apple.security.network.server</key>
	<true/>
	<key>com.apple.security.personal-information.location</key>
	<true/>
</dict>
</plist>


================================================
FILE: There/ThereApp.swift
================================================
//
//  ThereApp.swift
//  There
//
//  Created by Dena Sohrabi on 9/2/24.
//

import AppKit
import MenuBarExtraAccess
import PostHog
import SwiftUI
import UserNotifications

@main
struct ThereApp: App {
    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    @Environment(\.openWindow) var openWindow
    @ObservedObject var appState = AppState.shared
    @StateObject var router: Router = Router()
    var body: some Scene {
        MenuBarExtra {
            ContentView()
                .environment(\.database, .shared)
                .frame(width: 320)
                .frame(minHeight: 300)
                .frame(maxHeight: 600)
                .background(Color(NSColor.windowBackgroundColor).opacity(0.78).ignoresSafeArea())
                .environmentObject(appState)
                .environmentObject(router)
        } label: {
            let image: NSImage = {
                let ratio = $0.size.height / $0.size.width
                $0.size.height = 20
                $0.size.width = 20 / ratio
                return $0
            }(NSImage(named: "appIcon")!)

            Image(nsImage: image)
                .onAppear {
                    if UserDefaults.standard.bool(forKey: "hasCompletedInitialSetup") == false {
                        openWindow(id: "init")
                    }
                }
                .foregroundColor(.primary)
        }
        .menuBarExtraStyle(.window)
        .menuBarExtraAccess(isPresented: $appState.menuBarViewIsPresented)
        .windowResizability(.contentSize)

        WindowGroup("init", id: "init") {
            InitialView()
                .environment(\.database, .shared)
                .fixedSize()
                .frame(width: 600, height: 400)
                .environmentObject(appState)
        }
        .windowStyle(.hiddenTitleBar)
        .defaultSize(width: 600, height: 400)
        .defaultPosition(.center)
        .windowResizability(.contentSize)
        #if MAC_OS_VERSION_15_0
            .windowBackgroundDragBehavior(.enabled)
        #endif

        Settings {
            Text("Coming soon...")
        }
        #if MAC_OS_VERSION_15_0
            .windowStyle(.plain)
        #endif
            .defaultSize(width: 600, height: 400)
            .windowResizability(.automatic)
    }
}

extension EnvironmentValues {
    @Entry var database: AppDatabase = .shared
}

class AppState: ObservableObject {
    static let shared = AppState()
    @Published var menuBarViewIsPresented: Bool = false
    func presentMenu() {
        menuBarViewIsPresented = true
    }

    func hideMenu() {
        menuBarViewIsPresented = true
    }
}

class AppDelegate: NSObject, NSApplicationDelegate {
    func applicationDidFinishLaunching(_: Notification) {
        let POSTHOG_API_KEY = "phc_XZFRnJFd8RVNegex9sLKplgz8KCFxGyLZwxh5usmoig"
        let POSTHOG_HOST = "https://eu.i.posthog.com"

        let config = PostHogConfig(apiKey: POSTHOG_API_KEY, host: POSTHOG_HOST)

        PostHogSDK.shared.setup(config)
    }
}


================================================
FILE: There/UI/Button.swift
================================================
import SwiftUI

// PrimaryButton
struct PrimaryButton: View {
    var title: String
    var action: () -> Void

    var body: some View {
        Button(action: action) {
            Text(title)
        }
        .buttonStyle(PrimaryButtonStyle())
    }
}

struct PrimaryButtonStyle: ButtonStyle {
    let lightBlue = Color(red: 0.24, green: 0.67, blue: 0.91) // #3DAAE8
    let darkBlue = Color(red: 0.22, green: 0.60, blue: 0.82) // #3799D1

    @State private var hovered: Bool = false

    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .foregroundColor(.white)
            .fontWeight(.medium)
            .frame(width: 200, height: 32)
            .background(
                ZStack {
                    RoundedRectangle(cornerRadius: 8, style: .continuous)
                        .fill(hovered ? lightBlue.opacity(0.85) : lightBlue)
                    RoundedRectangle(cornerRadius: 8, style: .continuous)
                        .stroke(hovered ? darkBlue.opacity(0.6) : darkBlue, lineWidth: 3)
                }
            )
            .cornerRadius(8)
            .shadow(color: .primary.opacity(0.08), radius: 0.5, x: 0, y: configuration.isPressed ? 0 : (hovered ? 2 : 1))
            .scaleEffect(configuration.isPressed ? 0.98 : 1.0)
            .animation(.easeInOut(duration: 0.1), value: configuration.isPressed)
            .onHover { hovering in
                withAnimation {
                    self.hovered = hovering
                }
            }
    }
}

// SecondaryButton
struct SecondaryButton: View {
    var title: String
    var action: () -> Void

    var body: some View {
        Button(action: action) {
            Text(title)
                .foregroundColor(.primary)
        }
        .buttonStyle(SecondaryButtonStyle())
    }
}

struct SecondaryButtonStyle: ButtonStyle {
    @Environment(\.colorScheme) var scheme

    var white: Color {
        if scheme == .dark {
            return Color(.gray).opacity(0.2)
        } else {
            return Color(red: 1.0, green: 1.0, blue: 1.0)
        }
    }

    var lightGray: Color {
        if scheme == .dark {
            return Color(NSColor.systemGray).opacity(0.2)
        } else {
            return Color(red: 0.86, green: 0.86, blue: 0.86) // #DCDCDC
        }
    }

    @State private var hovered: Bool = false

    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .lineLimit(1)
            .padding(.horizontal, 6)
            .foregroundColor(.primary.opacity(0.8))
            .fontWeight(.medium)
            .frame(width: 200, height: 32)
            .background(
                ZStack {
                    RoundedRectangle(cornerRadius: 8, style: .continuous)
                        .fill(hovered ? white.opacity(0.85) : white)
                    RoundedRectangle(cornerRadius: 8, style: .continuous)
                        .stroke(hovered ? lightGray.opacity(0.6) : lightGray, lineWidth: 3)
                }
            )
            .cornerRadius(8)
            .shadow(color: .primary.opacity(0.08), radius: 0.5, x: 0, y: configuration.isPressed ? 0 : (hovered ? 2 : 1))
            .scaleEffect(configuration.isPressed ? 0.98 : 1.0)
            .animation(.easeInOut(duration: 0.1), value: configuration.isPressed)
            .onHover { hovering in
                withAnimation {
                    self.hovered = hovering
                }
            }
    }
}

// CompactButton
struct CompactButton: View {
    var title: String
    var action: () -> Void

    var body: some View {
        Button(action: action) {
            Text(title)
        }
        .buttonStyle(CompactButtonStyle())
    }
}

struct CompactButtonStyle: ButtonStyle {
    @Environment(\.colorScheme) var scheme

    var lightGray: Color {
        if scheme == .dark {
            return Color(NSColor.systemGray).opacity(0.2)
        } else {
            return Color(red: 0.86, green: 0.86, blue: 0.86) // #DCDCDC
        }
    }

    @State private var hovered: Bool = false

    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .padding(.horizontal, 8)
            .foregroundColor(.primary)
            .fontWeight(.medium)
            .frame(height: 28)
            .background(hovered ? (scheme == .dark ? Color(.gray).opacity(0.2) : .white) : (scheme == .dark ? Color(.gray).opacity(0.3) : .white.opacity(0.8)))
            .cornerRadius(8)
            .shadow(color: .primary.opacity(0.04), radius: 0.5, x: 0, y: configuration.isPressed ? 0 : (hovered ? 2 : 1))
            .scaleEffect(configuration.isPressed ? 0.98 : 1.0)
            .animation(.easeInOut(duration: 0.1), value: configuration.isPressed)
            .onHover { hovering in
                withAnimation {
                    self.hovered = hovering
                }
            }
    }
}

// CompactPrimaryButton
struct CompactPrimaryButton: View {
    var title: String
    var action: () -> Void
    var width: CGFloat = 232

    var body: some View {
        Button(action: action) {
            Text(title)
        }
        .buttonStyle(CompactPrimaryButtonStyle(width: width))
    }
}

struct CompactPrimaryButtonStyle: ButtonStyle {
    let lightBlue = Color(red: 0.24, green: 0.67, blue: 0.91) // #3DAAE8
    let darkBlue = Color(red: 0.22, green: 0.60, blue: 0.82) // #3799D1

    @State private var hovered: Bool = false
    var width: CGFloat

    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .foregroundColor(.white)
            .fontWeight(.medium)
            .frame(width: width, height: 32)
            .padding(.horizontal, 6)
            .background(
                ZStack {
                    RoundedRectangle(cornerRadius: 8, style: .continuous)
                        .fill(hovered ? lightBlue.opacity(0.85) : lightBlue)
                    RoundedRectangle(cornerRadius: 8, style: .continuous)
                        .stroke(hovered ? darkBlue.opacity(0.6) : darkBlue, lineWidth: 3)
                }
            )
            .cornerRadius(8)
            .shadow(color: .primary.opacity(0.08), radius: 0.5, x: 0, y: configuration.isPressed ? 0 : (hovered ? 2 : 1))
            .scaleEffect(configuration.isPressed ? 0.98 : 1.0)
            .animation(.easeInOut(duration: 0.1), value: configuration.isPressed)
            .onHover { hovering in
                withAnimation {
                    self.hovered = hovering
                }
            }
    }
}

// Preview
struct ButtonPreviews: PreviewProvider {
    static var previews: some View {
        VStack(spacing: 20) {
            PrimaryButton(title: "Primary Button", action: {})
            SecondaryButton(title: "Secondary Button", action: {})
            CompactButton(title: "Compact Button", action: {})
            CompactPrimaryButton(title: "Compact Primary Button", action: {})
        }
        .padding()
    }
}


================================================
FILE: There/UI/Heading.swift
================================================
import SwiftUI

struct Heading: View {
    let title: String

    var body: some View {
        Text(title)
            .font(.title2)
            .fontWeight(.medium)
    }
}

#Preview {
    Heading(title: "Hello, World!")
}


================================================
FILE: There/UI/Input.swift
================================================
import SwiftUI

struct AdaptiveColors {
    static let textFieldBackground = Color(.textBackgroundColor)
    static let textFieldBorder = Color.secondary
    static let textColor = Color.primary
}

struct Input: View {
    @Binding var text: String
    var placeholder: String
    @FocusState private var isFocused: Bool

    var body: some View {
        CustomTextInput(text: $text, placeholder: placeholder, isFocused: _isFocused)
            .frame(width: 200, height: 32)
    }
}

struct CompactInput: View {
    @Binding var text: String
    var placeholder: String
    @FocusState private var isFocused: Bool

    var body: some View {
        CustomTextInput(text: $text, placeholder: placeholder, isFocused: _isFocused)
            .frame(height: 32)
            .padding(.bottom)
            .scaledToFill()
    }
}

struct AutocompleteInput: View {
    @Binding var text: String
    let placeholder: String
    let suggestions: [String]
    let onCommit: () -> Void

    @State private var isEditing = false
    @State private var showSuggestions = false
    @FocusState private var isFocused: Bool

    var body: some View {
        VStack(alignment: .leading) {
            CustomTextInput(text: $text, placeholder: placeholder, isFocused: _isFocused)
                .frame(height: 32)
                .onChange(of: isFocused) { focused in
                    isEditing = focused
                    showSuggestions = focused && !suggestions.isEmpty
                }
                .onChange(of: text) { _ in
                    showSuggestions = isEditing && !suggestions.isEmpty
                }

            if showSuggestions {
                ScrollView {
                    LazyVStack(alignment: .leading) {
                        ForEach(suggestions.prefix(5), id: \.self) { suggestion in
                            Text(suggestion)
                                .padding(.vertical, 2)
                                .onTapGesture {
                                    text = suggestion
                                    showSuggestions = false
                                    onCommit()
                                }
                        }
                    }
                }
                .frame(maxHeight: 150)
                .background(AdaptiveColors.textFieldBackground)
                .cornerRadius(5)
                .shadow(color: Color.primary.opacity(0.2), radius: 5)
            }
        }
    }
}

struct CustomTextInput: View {
    @Binding var text: String
    var placeholder: String
    @FocusState var isFocused: Bool

    var body: some View {
        ZStack(alignment: .leading) {
            RoundedRectangle(cornerRadius: 8)
                .fill(AdaptiveColors.textFieldBackground)

            RoundedRectangle(cornerRadius: 8)
                .stroke(isFocused ? .blue : AdaptiveColors.textFieldBorder.opacity(0.5), lineWidth: 1)

            TextField(placeholder, text: $text)
                .textFieldStyle(.plain)
                .padding(.horizontal, 6)
                .foregroundColor(AdaptiveColors.textColor)
                .focused($isFocused)
        }
        .onTapGesture {
            isFocused = true
        }
    }
}


================================================
FILE: There/UI/Label.swift
================================================
import SwiftUI

struct StyledLabel: View {
    let title: String
    
    var body: some View {
        Text(title)
            .font(.caption)
            .fontWeight(.semibold)
            .foregroundColor(.secondary)
    }
}

#Preview {
    StyledLabel(title: "Name")
}


================================================
FILE: There/UI/LocalImageView.swift
================================================
import SwiftUI

struct LocalImageView: View {
    let imageURL: URL

    @State private var image: NSImage?
    @State private var isLoading = true
    @State private var errorMessage: String?

    var body: some View {
        Group {
            if let image = image {
                Image(nsImage: image)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
            } else if isLoading {
                ProgressView()
            } else if let errorMessage = errorMessage {
                Text(errorMessage)
                    .foregroundColor(.red)
            }
        }
        .onAppear(perform: loadImage)
    }

    private func loadImage() {
        isLoading = true
        DispatchQueue.global(qos: .userInitiated).async {
            if let imageData = try? Data(contentsOf: imageURL),
               let loadedImage = NSImage(data: imageData) {
                DispatchQueue.main.async {
                    self.image = loadedImage
                    self.isLoading = false
                }
            } else {
                DispatchQueue.main.async {
                    self.errorMessage = "Failed to load image"
                    self.isLoading = false
                }
            }
        }
    }
}


================================================
FILE: There/UI/Titlebar.swift
================================================
import SwiftUI

struct Titlebar: View {
    @EnvironmentObject var router: Router
    @State var hovered: Bool = false
    var body: some View {
        HStack(alignment: .center) {
            Button {
                router.cleanActiveRoute()
            } label: {
                Image(systemName: "chevron.left")
                    .foregroundColor(.secondary)
                    .font(.body)
                    .padding(4)
                    .background(hovered ? Color(NSColor.separatorColor).opacity(0.8) : .clear)
                    .cornerRadius(6)
                    .onHover { hovered in
                        withAnimation {
                            self.hovered = hovered
                        }
                    }
            }
            .buttonStyle(.plain)

            switch router.activeRoute {
            case .addTimezone:
                Text("🗺️")
                    .font(.callout)
                Text("Add Time Zone")
                    .font(.body)
                    .fontWeight(.medium)
            case .editTimeZone:
                Text("✍️")
                    .font(.callout)
                Text("Edit Time Zone")
                    .font(.body)
                    .fontWeight(.medium)
            case .mainView:
                EmptyView()
            }
        }
    }
}

#Preview {
    Titlebar()
        .padding()
}


================================================
FILE: There/UI/TransparentBackgroundView.swift
================================================
import SwiftUI

struct TransparentBackgroundView: NSViewRepresentable {
    func makeNSView(context: Context) -> NSVisualEffectView {
        let view = NSVisualEffectView()
        view.blendingMode = .withinWindow
        view.state = .active
        view.material = .popover
        return view
    }

    func updateNSView(_ nsView: NSVisualEffectView, context: Context) {}
}

#Preview {
    TransparentBackgroundView()
}


================================================
FILE: There/Views/ButtomBar/AddButton.swift
================================================
import SwiftUI

struct AddButton: View {
    @State private var addHovered: Bool = false
    @EnvironmentObject var appState: AppState
    @EnvironmentObject var router: Router

    var body: some View {
        CompactButton(title: "Add") {
            router.setActiveRoute(to: .addTimezone)
        }
    }
}

#Preview {
    AddButton()
        .padding()
}


================================================
FILE: There/Views/ButtomBar/BottomBarView.swift
================================================
import SwiftUI

struct BottomBarView: View {
    @Binding var isAtBottom: Bool
    @Binding var sortOrder: SortOrder
    @Binding var showSlider: Bool

    var body: some View {
        HStack(spacing: 2) {
            AddButton()
            Spacer()

            Button {
                withAnimation {
                    showSlider.toggle()
                }
            } label: {
                Image(systemName: "clock.arrow.2.circlepath")
                    .font(.body)
            }
            .buttonStyle(SettingsButtonStyle())
            .help("Time Slider")

            SettingsButton(sortOrder: $sortOrder)
        }
        .padding(.horizontal, 8)
        .frame(maxWidth: .infinity)
        .frame(height: 45)
        .overlay(alignment: .top) {
            if isAtBottom {
                Divider()
                    .padding(.top, -2)
            }
        }
        .animation(.default, value: isAtBottom)
    }
}

#Preview {
    BottomBarView(isAtBottom: .constant(true), sortOrder: .constant(.timeDescending), showSlider: .constant(false))
}


================================================
FILE: There/Views/ButtomBar/SettingsButton.swift
================================================
import SwiftUI

struct SettingsButton: View {
    @State private var settingsHovered: Bool = false
    @Environment(\.openWindow) var openWindow
    @Environment(\.openURL) var openURL
    @EnvironmentObject var appState: AppState
    @Environment(\.database) var database: AppDatabase
    @Environment(\.colorScheme) var scheme
    @AppStorage("launchAtLogin") private var launchAtLogin = false
    @Binding var sortOrder: SortOrder
    var backgroundColor: Color {
        if scheme == .dark {
            return Color(.gray).opacity(0.2)
        } else {
            return .white
        }
    }

    var body: some View {
        Menu {
            Toggle("Launch at Login", isOn: $launchAtLogin)
                .onChange(of: launchAtLogin) { newValue in
                    if newValue {
                        installLaunchAgent()
                    } else {
                        uninstallLaunchAgent()
                    }
                }
            Toggle("Ascending order", isOn: Binding(
                get: { sortOrder == .timeAscending },
                set: { newValue in
                    sortOrder = newValue ? .timeAscending : .timeDescending
                }
            ))

            Section("Support") {
                Button("DM on X") {
                    openURL(URL(string: "https://twitter.com/messages/compose?recipient_id=1434101346110689282")!)
                }
                Button("Email Us") {
                    openAppleMailComposer(to: "support@there.pm",
                                          subject: "Support Request",
                                          body: "Hello, I need assistance with...")
                }
            }
            Section {
                Button("Open Website") {
                    openURL(URL(string: "https://there.pm")!)
                }
                Button("Follow on X") {
                    openURL(URL(string: "https://x.com/ThereHQ")!)
                }
            }

            #if targetEnvironment(simulator) || DEBUG
                Section("Dev & Debug") {
                    Button("Clear Cache & Data") {
                        UserDefaults.standard.removeObject(forKey: "hasCompletedInitialSetup")
                        do {
                            _ = try database.dbWriter.write { db in
                                try Entry.deleteAll(db)
                            }
                        } catch {
                            print("Can't clear DB \(error)")
                        }
                    }
                }
            #endif

            Divider()

            Button("Quit") {
                NSApplication.shared.terminate(nil)
            }
            .keyboardShortcut("q", modifiers: .command)
        } label: {
            Image(systemName: "gearshape.fill")
                .font(.body)
        }
        .buttonStyle(SettingsButtonStyle())
    }

    func openAppleMailComposer(to recipient: String, subject: String? = nil, body: String? = nil) {
        let appleMailBundleIdentifier = "com.apple.mail"

        guard let appleMailURL = NSWorkspace.shared.urlForApplication(withBundleIdentifier: appleMailBundleIdentifier) else {
            print("Apple Mail not found")
            return
        }

        var components = URLComponents()
        components.scheme = "mailto"
        components.path = recipient

        var queryItems = [URLQueryItem]()
        if let subject = subject {
            queryItems.append(URLQueryItem(name: "subject", value: subject))
        }
        if let body = body {
            queryItems.append(URLQueryItem(name: "body", value: body))
        }

        if !queryItems.isEmpty {
            components.queryItems = queryItems
        }

        guard let emailURL = components.url else {
            print("Invalid email URL")
            return
        }

        let configuration = NSWorkspace.OpenConfiguration()
        configuration.activates = true

        NSWorkspace.shared.open([emailURL], withApplicationAt: appleMailURL, configuration: configuration) { _, error in
            if let error = error {
                print("Failed to open Apple Mail: \(error.localizedDescription)")
            }
        }
    }
}

#Preview {
    SettingsButton(sortOrder: .constant(.timeAscending))
}

struct SettingsButtonStyle: ButtonStyle {
    @Environment(\.colorScheme) var scheme
    @State private var isHovered = false

    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .foregroundColor(isHovered ? .primary : .secondary)
            .frame(height: 28)
            .padding(.horizontal, 8)
            .background(isHovered ? backgroundColor : .clear)
            .cornerRadius(8)
            .onHover { hovering in
                withAnimation {
                    isHovered = hovering
                }
            }
    }

    private var backgroundColor: Color {
        scheme == .dark ? Color(.gray).opacity(0.2) : .white
    }
}


================================================
FILE: There/Views/CLAuthorizationStatus+Description.swift 
================================================
import CoreLocation

extension CLAuthorizationStatus: CustomStringConvertible {
    public var description: String {
        switch self {
        case .notDetermined: return "Not Determined"
        case .restricted: return "Restricted"
        case .denied: return "Denied"
        case .authorizedAlways: return "Authorized Always"
        @unknown default: return "Unknown"
        }
    }
}


================================================
FILE: There/Views/EmptyTimezoneView.swift
================================================
import SwiftUI

struct EmptyTimezoneView: View {
    var body: some View {
        VStack {
            Image("Earth")
                .resizable()
                .frame(width: 158, height: 140)
                .padding(.bottom, 8)
                .padding(.leading, -43)
            Text("Hey There!")
                .font(.title)
                .fontWeight(.medium)

            Text("Add your first timezone")
                .foregroundColor(.secondary)
        }
    }
}

#Preview {
    EmptyTimezoneView()
        .frame(width: 320, height: 320)
}


================================================
FILE: There/Views/EntryUI/EntryIcon.swift
================================================
import CoreLocation
import SwiftUI

struct EntryIcon: View {
    let entry: Entry
    @Environment(\.colorScheme) var scheme
    @Environment(\.database) var database
    @State private var useClockIcon: Bool = false

    var backgroundColor: Color {
        if scheme == .dark {
            return Color(NSColor.darkGray)
        } else {
            return Color.white.opacity(0.6)
        }
    }

    var body: some View {
        Group {
            if let data = entry.photoData, !data.isEmpty {
                photoIcon(data: data)
            } else if let flag = entry.flag {
                placeIcon
            } else {
                defaultIcon
            }
        }
        .overlay(alignment: .bottomTrailing) {
            timeIcon
        }
        .onAppear {
            if entry.flag == nil || entry.flag!.isEmpty {
                searchForFlag()
            }
        }
    }

    private var placeIcon: some View {
        Circle()
            .fill(backgroundColor)
            .frame(width: 45)
            .overlay {
                if let flag = entry.flag {
                    Text(flag)
                        .font(.largeTitle)
                } else {
                    Image(systemName: "clock")
                        .font(.largeTitle)
                        .foregroundColor(.secondary)
                }
            }
    }

    private func photoIcon(data: String) -> some View {
        Group {
            if let url = URL(string: data) {
                AsyncImage(url: url) { phase in
                    switch phase {
                    case let .success(image):
                        image
                            .resizable()
                            .aspectRatio(contentMode: .fill)
                            .frame(width: 45, height: 45)
                            .clipShape(Circle())
                    case .failure:
                        defaultIcon
                    case .empty:
                        ProgressView()
                    @unknown default:
                        defaultIcon
                    }
                }
            } else {
                defaultIcon
            }
        }
    }

    private var defaultIcon: some View {
        Circle()
            .fill(backgroundColor)
            .frame(width: 45)
            .overlay {
                if let flag = entry.flag, !flag.isEmpty {
                    Text(flag)
                        .font(.largeTitle)
                } else {
                    Image(systemName: "clock")
                        .font(.largeTitle)
                        .foregroundColor(.secondary)
                }
            }
    }

    private var timeIcon: some View {
        Image(entry.timeIcon)
            .resizable()
            .aspectRatio(contentMode: .fit)
            .frame(width: 14, height: 14)
            .background(
                TransparentBackgroundView()
                    .frame(width: 18, height: 18)
                    .cornerRadius(50)
            )
            .padding(.bottom, 4)
            .padding(.trailing, -3)
    }

    func searchForFlag() {
        let geocoder = CLGeocoder()
        geocoder.geocodeAddressString(entry.city) { placemarks, _ in
            if let placemark = placemarks?.first, let timezone = placemark.timeZone {
                Task {
                    do {
                        try await database.dbWriter.write { db in
                            var entry = try Entry.fetchOne(db, id: entry.id)
                            let countryEmoji = Utils.shared.getCountryEmoji(for: placemark.isoCountryCode ?? "")
                            entry?.flag = countryEmoji.isEmpty ? "🌍" : countryEmoji
                            try entry?.update(db)
                        }
                    } catch {
                        print("Can't find flag \(error)")
                    }
                }
            }
        }
    }
}


================================================
FILE: There/Views/EntryUI/EntryRow.swift
================================================
import Combine
import SwiftUI

struct EntryRow: View {
    let entry: Entry
    @State private var isHovered: Bool = false
    @Environment(\.colorScheme) var scheme
    @EnvironmentObject var router: Router
    @Environment(\.scenePhase) private var scenePhase
    @State private var currentDate = Date()
    @State private var timer: Publishers.Autoconnect<Timer.TimerPublisher>?
    @Binding var timeOffset: Double

    var body: some View {
        HStack {
            EntryIcon(entry: entry)
            VStack(alignment: .leading) {
                Text(entry.name.isEmpty ? entry.city : entry.name)
                    .font(.title3)
                    .fontWeight(.medium)
                    .lineLimit(1)

                Text(entry.city)
                    .font(.body)
                    .foregroundColor(.secondary)
                    .lineLimit(1)
            }
            .padding(.leading, 6)
            Spacer()
            VStack(alignment: .trailing) {
                HStack(spacing: 0) {
                    Text(formattedTime(timeZoneIdentifier: entry.timezoneIdentifier))
                        .monospaced()
                        .font(.body)
                        .contentTransition(.numericText())
                }
                Text(formatTimeDifference())
                    .monospaced()
                    .font(.callout)
                    .foregroundColor(.gray)
            }
        }
        .frame(maxWidth: .infinity, alignment: .leading)
        .padding(.horizontal, 8)
        .padding(.vertical, 6)
        .background(isHovered ? scheme == .dark ? Color.white.opacity(0.1) : Color.white.opacity(0.6) : Color.clear)
        .cornerRadius(8)
        .onHover { isHovered in
            withAnimation(.easeInOut(duration: 0.1)) {
                self.isHovered = isHovered
            }
        }
        .onReceive(timer ?? Timer.publish(every: 1, on: .main, in: .common).autoconnect()) { _ in
            currentDate = Date()
        }
        .onTapGesture {
            router.setActiveRoute(to: .editTimeZone(entryId: entry.id))
        }
        .onChange(of: scenePhase) { newPhase in
            updateTimer(for: newPhase)
        }
        .onAppear {
            updateTimer(for: scenePhase)
        }
    }

    private func formattedTime(timeZoneIdentifier: String) -> String {
        let formatter = DateFormatter()
        formatter.timeZone = TimeZone(identifier: timeZoneIdentifier)

        // Get the system's locale
        let locale = Locale.current

        // Create a template that includes both 24-hour and 12-hour formats
        let template = "j:mm"

        // Generate the best format for the current locale
        if let formatString = DateFormatter.dateFormat(fromTemplate: template, options: 0, locale: locale) {
            formatter.dateFormat = formatString
        } else {
            // Fallback to a default format if generation fails
            formatter.timeStyle = .short
        }
        let offsetDate = Date().addingTimeInterval(timeOffset * 3600)

        return formatter.string(from: offsetDate)
    }

    private func formatTimeDifference() -> String {
        let userTimeZone = TimeZone.current
        let entryTimeZone = TimeZone(identifier: entry.timezoneIdentifier) ?? .current

        let userDate = currentDate.addingTimeInterval(TimeInterval(userTimeZone.secondsFromGMT()))
        let entryDate = currentDate.addingTimeInterval(TimeInterval(entryTimeZone.secondsFromGMT()))

        let difference = Calendar.current.dateComponents([.hour, .minute], from: userDate, to: entryDate)

        let hours = difference.hour ?? 0
        let minutes = difference.minute ?? 0

        if hours == 0 && minutes == 0 {
            return "same time"
        }

        let totalHours = Double(hours) + Double(minutes) / 60.0

        return String(format: "%+.1f hrs", totalHours)
    }

    private func updateTimer(for phase: ScenePhase) {
        timer?.upstream.connect().cancel()

        switch phase {
        case .active:
            timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
        case .inactive, .background:
            timer = Timer.publish(every: 60, on: .main, in: .common).autoconnect()
        @unknown default:
            timer = Timer.publish(every: 60, on: .main, in: .common).autoconnect()
        }
    }
}


================================================
FILE: There/Views/MainView.swift
================================================
import AppKit
import GRDB
import PostHog
import SwiftUI

struct MainView: View {
    @StateObject private var fetcher = Fetcher()
    @State private var sortOrder: SortOrder = .timeAscending
    @State private var sortedEntries: [Entry] = []
    @Environment(\.database) var database: AppDatabase
    @EnvironmentObject var appState: AppState
    @EnvironmentObject var router: Router
    @State private var currentDate = Date()
    @State private var isAtBottom: Bool = false
    @State private var showSlider: Bool = false
    @State var timeOffset: Double = 0

    var body: some View {
        VStack(alignment: sortedEntries.isEmpty ? .center : .leading, spacing: 2) {
            if sortedEntries.isEmpty {
                Spacer()
                EmptyTimezoneView()
                Spacer()
            } else {
                ScrollView(.vertical) {
                    LazyVStack(spacing: 0) {
                        ForEach(sortedEntries) { entry in
                            EntryRow(entry: entry, timeOffset: $timeOffset)
                                .contextMenu {
                                    Button("Edit") {
                                        router.setActiveRoute(to: .editTimeZone(entryId: entry.id))
                                    }
                                    Button("Delete", role: .destructive) {
                                        deleteEntry(entry)
                                    }
                                }

                                .id(entry.id)
                        }
                        .animation(.easeInOut(duration: 0.1), value: sortedEntries.count)
                        Color.clear
                            .frame(height: 2)
                            .onAppear {
                                isAtBottom = false
                            }
                            .onDisappear {
                                isAtBottom = true
                            }
                    }
                    .padding(.horizontal, 6)
                }
                .scrollIndicators(.hidden)
            }
            if showSlider {
                EntryTimeSlider(timeOffset: $timeOffset)
                    .onDisappear {
                        withAnimation {
                            timeOffset = 0.0
                        }
                    }
            }
            BottomBarView(isAtBottom: $isAtBottom, sortOrder: $sortOrder, showSlider: $showSlider)
        }
        .frame(maxHeight: .infinity)
        .padding(.top, 6)
        .onAppear {
            sortEntries()
        }
        .onChange(of: sortOrder) { _ in
            sortEntries()
        }
        .onChange(of: fetcher.entries) { _ in
            sortEntries()
        }
        .task {
            let id = PostHogSDK.shared.getAnonymousId()
            PostHogSDK.shared.identify(id, userProperties: ["email": UserDefaults.standard.string(forKey: "userEmail") ?? ""])
        }

    }

    private func sortEntries() {
        switch sortOrder {
        case .timeAscending:
            sortedEntries = fetcher.entries.sorted { $0.timeDifference.hours < $1.timeDifference.hours }
        case .timeDescending:
            sortedEntries = fetcher.entries.sorted { $0.timeDifference.hours > $1.timeDifference.hours }
        }
    }

    private func deleteEntry(_ entry: Entry) {
        Task {
            do {
                try await database.dbWriter.write { db in
                    let fetchedEntry = try Entry.fetchOne(db, id: entry.id)
                    try fetchedEntry?.delete(db)
                }
            } catch {
                print("Can't delete entry \(error)")
            }
        }
    }
}

#Preview {
    MainView()
        .frame(width: 400, height: 400)
}

struct EntryTimeSlider: View {
    @Binding var timeOffset: Double
    @State private var previousValue: Double = 0
    @State var currentHour: Double = Date().hour

    var offset: String {
        if timeOffset == 1 || timeOffset == -1 {
            return "\(timeOffset > 0 ? "+" : "") \(timeOffset) hr"
        } else {
            return "\(timeOffset > 0 ? "+" : "")\(timeOffset) hrs"
        }
    }

    var body: some View {
        VStack(spacing: 8) {
            HStack {
                Text("\(offset)")
                    .monospaced()
                    .font(.callout)
                    .foregroundColor(.gray)
                    .contentTransition(.numericText())
                Spacer()
                Text(formattedTime())
                    .monospaced()
                    .font(.body)
                    .fontWeight(.semibold)
                    .contentTransition(.numericText())
            }
            Slider(value: $currentHour, in: 0 ... 23.5, step: 0.5)
                .onChange(of: currentHour) { newValue in
                    withAnimation {
                        timeOffset = newValue - Date().hour
                        if Int(newValue) != Int(previousValue) {
                            performHapticFeedback()
                        }
                        previousValue = newValue
                    }
                }
        }
        .padding(.vertical, 8)
        .padding(.horizontal, 16)
        .background(Color(NSColor.windowBackgroundColor))
    }

    private func formattedTime() -> String {
        let calendar = Calendar.current
        let now = Date()

        let formatter = DateFormatter()

        // Get the system's locale
        let locale = Locale.current

        // Create a template that includes both 24-hour and 12-hour formats
        let template = "j:mm"

        // Generate the best format for the current locale
        if let formatString = DateFormatter.dateFormat(fromTemplate: template, options: 0, locale: locale) {
            formatter.dateFormat = formatString
        } else {
            // Fallback to a default format if generation fails
            formatter.timeStyle = .short
        }

        formatter.locale = locale
        let offsetDate = Date().addingTimeInterval(timeOffset * 3600)

        return formatter.string(from: offsetDate)
    }

    private func performHapticFeedback() {
        NSHapticFeedbackManager.defaultPerformer.perform(.levelChange, performanceTime: .default)
    }
}

extension Date {
    var hour: Double {
        Double(Calendar.current.component(.hour, from: self)) + (Calendar.current.component(.minute, from: self) >= 30 ? 0.5 : 0.0)
    }
}


================================================
FILE: There/Views/Onboarding/InitialView.swift
================================================
// InitialView.swift

import PostHog
import SwiftUI

struct InitialView: View {
    @Environment(\.database) var database
    @State private var email: String = ""
    @EnvironmentObject var appState: AppState

    var body: some View {
        HStack(spacing: 80) {
            LeftPanel()
            RightPanel(email: $email, saveEmail: saveEmail)
        }
        .padding()
    }

    func signupForThere(email: String, completion: @escaping (Bool) -> Void) {
        var hostname: String {
            #if DEBUG
                return "http://localhost:8000"
            #else
                return "https://headline.inline.chat"
            #endif
        }
        let url = URL(string: "\(hostname)/api/there/signup")!
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")

        let timeZone = TimeZone.current.identifier
        let body: [String: Any] = ["email": email, "timeZone": timeZone]

        request.httpBody = try? JSONSerialization.data(withJSONObject: body)

        URLSession.shared.dataTask(with: request) { _, response, _ in
            let success = (response as? HTTPURLResponse)?.statusCode == 200
            DispatchQueue.main.async {
                completion(success)
            }
        }.resume()
    }

    func saveEmail() {
        signupForThere(email: email) { success in
            if success {
                PostHogSDK.shared.capture("user_signed_up",
                                          userProperties: ["email": email],
                                          userPropertiesSetOnce: ["date_of_sign_up": Date.now])
                print("Signup successful")
                UserDefaults.standard.set(email, forKey: "userEmail")
                UserDefaults.standard.set(true, forKey: "hasCompletedInitialSetup")

            } else {
                print("Signup failed")
                UserDefaults.standard.set(true, forKey: "hasCompletedInitialSetup")
            }
        }
    }
}

#Preview {
    InitialView()
        .frame(width: 600, height: 400)
}


================================================
FILE: There/Views/Onboarding/LeftPanel.swift
================================================
// LeftPanel.swift

import SwiftUI

struct LeftPanel: View {
    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            Image("Logo")
            Text("Hey There!")
                .font(.largeTitle)
                .fontWeight(.bold)
            DateTimeInfo()
        }
    }
}

struct DateTimeInfo: View {
    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            HStack {
                DateTimeLabel(date: Date.now, format: .dateTime.timeZone() , color: .green)
                DateTimeLabel(date: Date.now, format: .dateTime.hour().minute(), color: .cyan)
            }
            HStack {
                DateTimeLabel(text: TimeZone.current.identifier, color: .yellow)
                DateTimeLabel(date: Date.now, format: .dateTime.weekday(), color: .pink)
            }
        }
    }
}

struct DateTimeLabel: View {
    var date: Date?
    var text: String?
    var format: Date.FormatStyle?
    var color: Color

    init(date: Date? = nil, text: String? = nil, format: Date.FormatStyle? = nil, color: Color = .green) {
        self.date = date
        self.text = text
        self.format = format
        self.color = color
    }

    var body: some View {
        Group {
            if let date = date, let format = format {
                Text(date, format: format)
            } else if let text = text {
                Text(text)
            } else {
                Text("Invalid input")
            }
        }
        .monospaced()
        .padding(4)
        .background(color.opacity(0.2))
        .cornerRadius(8)
    }
}


================================================
FILE: There/Views/Onboarding/RightPanel.swift
================================================
// RightPanel.swift

import PostHog
import SwiftUI

struct RightPanel: View {
    @Binding var email: String
    let saveEmail: () -> Void
    @EnvironmentObject var appState: AppState
    @Environment(\.presentationMode) var presentationMode

    var body: some View {
        VStack(alignment: .leading, spacing: 4) {
            Text("Please enter your email")
                .font(.callout)
                .fontWeight(.medium)
                .padding(.bottom, 2)

            Input(text: $email, placeholder: "dena@example.com")

            PrimaryButton(title: "Continue", action: {
                saveEmail()
                appState.presentMenu()
                presentationMode.wrappedValue.dismiss()

            })

            SecondaryButton(title: "Skip", action: {
                PostHogSDK.shared.capture("sign_up_skipped", properties: ["date": Date.now])
                UserDefaults.standard.setValue(true, forKey: "hasCompletedInitialSetup")
                appState.presentMenu()
                presentationMode.wrappedValue.dismiss()
            })
        }
    }
}


================================================
FILE: There/Views/Timezone/AddTimezone + Components.swift
================================================
import AppKit
import MapKit
import SwiftUI

// MARK: - IconView

struct IconView: View {
    @Binding var image: NSImage?
    @Binding var countryEmoji: String

    @State private var showPopover = false
    @State private var isDropTargeted = false
    @State private var showingXAccountInput = false
    @State private var showingTGAccountInput = false
    @State private var username = ""
    @State private var debounceTask: Task<Void, Never>?

    var body: some View {
        iconContent
            .frame(width: 70, height: 70)
            .onTapGesture { showPopover = true }
            .popover(isPresented: $showPopover) { popoverContent }
    }

    private var iconContent: some View {
        Group {
            if let image = image {
                Image(nsImage: image)
                    .resizable()
                    .scaledToFill()
                    .clipShape(Circle())
            } else if !countryEmoji.isEmpty {
                flagView
            } else {
                placeholderView
            }
        }
    }

    private var flagView: some View {
        Circle()
            .fill(.white)
            .overlay(Text(countryEmoji).font(.largeTitle))
    }

    private var placeholderView: some View {
        Circle()
            .fill(.gray.opacity(0.1))
            .overlay(
                Image(systemName: "photo.badge.plus")
                    .font(.title)
                    .foregroundColor(.gray.opacity(0.8))
            )
    }

    private var popoverContent: some View {
        VStack {
            importButtons

            if showingXAccountInput {
                SocialMediaInput(platform: "X", username: $username, image: $image, debounceTask: $debounceTask)
                    .onSubmit {
                        showPopover = false
                    }
            } else if showingTGAccountInput {
                SocialMediaInput(platform: "Telegram", username: $username, image: $image, debounceTask: $debounceTask)
                    .onSubmit {
                        showPopover = false
                    }
            }
        }
        .padding()
    }

    private var importButtons: some View {
        HStack(alignment: .center, spacing: 0) {
            Text("Set from").foregroundColor(.secondary)
            Button("Telegram") {
                showingXAccountInput = false
                showingTGAccountInput = true
            }
            .buttonStyle(.link)
            .padding(.horizontal, 2)
            Text("/").foregroundColor(.secondary).padding(.horizontal, 2)
            Button("X") {
                showingTGAccountInput = false
                showingXAccountInput = true
            }
            .buttonStyle(.link)
            .padding(.horizontal, 2)
            Text("/").foregroundColor(.secondary).padding(.horizontal, 2)
            Button("Finder") {
                let selectedImage = Utils.shared.selectPhoto()
                DispatchQueue.main.async {
                    self.image = selectedImage
                }

            }.buttonStyle(.link).padding(.horizontal, 2)
        }
    }

    private func handleDrop(providers: [NSItemProvider]) -> Bool {
        guard let provider = providers.first else { return false }

        provider.loadObject(ofClass: NSImage.self) { object, _ in
            if let image = object as? NSImage {
                DispatchQueue.main.async {
                    self.image = image
                }
            }
        }
        return true
    }
}

struct CitySearchResultRow: View, Equatable {
    let result: TimeZoneSearchResult

    var body: some View {
        HStack {
            VStack(alignment: .leading) {
                Text(result.title)

                Text(result.subtitle ?? "")
                    .font(.caption)
                    .foregroundColor(.secondary)
            }
            Spacer()
        }
        .frame(maxWidth: .infinity)
        .contentShape(Rectangle())
    }

    static func == (lhs: CitySearchResultRow, rhs: CitySearchResultRow) -> Bool {
        lhs.result == rhs.result
    }
}

// MARK: - CitySearchResults

struct CitySearchResults: View {
    @ObservedObject var searchCompleter: SearchCompleter
    @Binding var isShowingPopover: Bool
    @Binding var selectedCity: String
    @Binding var selectedTimezone: TimeZone?
    @Binding var countryEmoji: String
    @FocusState private var isFocused: Bool
    @State private var selectedIndex: Int = -1

    var body: some View {
        VStack(spacing: 0) {
            CustomTextField(text: $searchCompleter.queryFragment, placeholder: "Search for a city or timezone", onKeyDown: handleKeyEvent)
                .textFieldStyle(.roundedBorder)
                .padding(.horizontal, 6)
                .frame(width: 280, height: 32)
                .background(AdaptiveColors.textFieldBackground)
                .cornerRadius(8)
                .overlay(
                    RoundedRectangle(cornerRadius: 8)
                        .stroke(isFocused ? .blue : AdaptiveColors.textFieldBorder.opacity(0.5), lineWidth: 1)
                )
                .focused($isFocused)
                .foregroundColor(AdaptiveColors.textColor)
                .padding(.vertical)

            ScrollViewReader { proxy in
                List(searchCompleter.results.indices, id: \.self) { index in
                    let result = searchCompleter.results[index]

                    Button(action: {
                        self.selectCity(result)
                    }) {
                        CitySearchResultRow(result: result)
                    }
                    .buttonStyle(.plain)
                    .listRowBackground(selectedIndex == index ? Color.accentColor.opacity(0.1) : Color.clear)
                    .id(index)
                }
                .listStyle(PlainListStyle())
                .onChange(of: selectedIndex) { newValue in
                    if newValue >= 0 {
                        // This handles all scrolling scenarios, including scrolling to the bottom
                        // when the last item is selected:
                        // 1. It triggers whenever selectedIndex changes.
                        // 2. It scrolls to the newly selected item if it's in the list (index >= 0).
                        // 3. The .center anchor attempts to center the item in the visible area.
                        // 4. For the last item, this effectively scrolls to the bottom of the list.
                        // 5. It also ensures that the selected item is always visible, even if it's
                        //    not the last item.
                        proxy.scrollTo(newValue, anchor: .center)
                    }
                }
            }
        }
        .frame(width: 300, height: 400)
    }

    private func handleKeyEvent(_ event: NSEvent) -> Bool {
        switch event.keyCode {
        case 126: // Up arrow
            moveSelection(direction: .up)
            return true
        case 125: // Down arrow
            moveSelection(direction: .down)
            return true
        case 36: // Return key
            if selectedIndex >= 0 && selectedIndex < searchCompleter.results.count {
                selectCity(searchCompleter.results[selectedIndex])
            }
            return true
        default:
            return false
        }
    }

    private func moveSelection(direction: KeyboardNavigationDirection) {
        let itemCount = searchCompleter.results.count
        switch direction {
        case .up:
            if selectedIndex > 0 {
                // If not at the top of the list, move up one item
                selectedIndex -= 1
            } else if selectedIndex == 0 {
                // If at the top of the list, move focus to the search field
                selectedIndex = -1
            } else if selectedIndex == -1 {
                // If focus is on the search field, move to the bottom of the list
                selectedIndex = itemCount - 1
            }
        case .down:
            if selectedIndex == -1 {
                // If focus is on the search field and there are items, select the first item
                if itemCount > 0 {
                    selectedIndex = 0
                }
            } else if selectedIndex < itemCount - 1 {
                // If not at the bottom of the list, move down one item
                selectedIndex += 1
            } else if selectedIndex == itemCount - 1 {
                // If at the bottom of the list, move focus back to the search field
                selectedIndex = -1
            }
        case .enter:
            // Enter key handling is done elsewhere
            break
        }
    }

    private func selectCity(_ result: TimeZoneSearchResult) {
        switch result.type {
        case .city:
            selectedCity = "\(result.title), \(result.subtitle)"

            Task {
                if let timezone = await result.getTimeZone() {
                    await MainActor.run {
                        selectedTimezone = timezone
                        let geocoder = CLGeocoder()
                        geocoder.geocodeAddressString(result.title) { [self] placemarks, _ in
                            if let placemark = placemarks?.first, let timezone = selectedTimezone {
                                countryEmoji = Utils.shared.getCountryEmoji(for: placemark.isoCountryCode ?? "")
                            }
                        }
                    }

                  
                } else {
                    await MainActor.run {
                        fallbackToGeocoding(for: selectedCity)
                    }
                }
            }

        case .abbreviation:
            selectedCity = result.title
            selectedTimezone = result.identifier.flatMap { TimeZone(identifier: $0) }
            countryEmoji = ""

        case .utcOffset:
            selectedCity = result.title
            selectedTimezone = result.identifier.flatMap { TimeZone(identifier: $0) }
            countryEmoji = ""
        }

        isShowingPopover = false
    }

    private func fallbackToGeocoding(for address: String) {
        let geocoder = CLGeocoder()
        geocoder.geocodeAddressString(address) { [self] placemarks, _ in
            if let placemark = placemarks?.first, let timezone = placemark.timeZone {
                selectedTimezone = timezone
                countryEmoji = Utils.shared.getCountryEmoji(for: placemark.isoCountryCode ?? "")
            }
        }
    }

//    private func selectCity(_ result: TimeZoneSearchResult) {
//
//        selectedCity = "\(result.title), \(result.subtitle)"
//        selectedTimezone = result.getTimeZone()
//
//        if result.type == .city {
//            let geocoder = CLGeocoder()
//            geocoder.geocodeAddressString(selectedCity) { placemarks, _ in
//                if let placemark = placemarks?.first {
//                    countryEmoji = Utils.shared.getCountryEmoji(for: placemark.isoCountryCode ?? "")
//                }
//            }
//        } else {
//            countryEmoji = ""
//        }
//
//        isShowingPopover = false
//    }
}

struct CustomTextField: NSViewRepresentable {
    @Binding var text: String
    var placeholder: String
    var onKeyDown: (NSEvent) -> Bool

    func makeNSView(context: Context) -> NSTextField {
        let textField = NSTextField()
        textField.placeholderString = placeholder
        textField.delegate = context.coordinator
        textField.focusRingType = .none
        textField.drawsBackground = true
        textField.backgroundColor = .white
        textField.isBordered = false
        textField.textColor = .black
        return textField
    }

    func updateNSView(_ nsView: NSTextField, context: Context) {
        nsView.stringValue = text
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject, NSTextFieldDelegate {
        var parent: CustomTextField

        init(_ parent: CustomTextField) {
            self.parent = parent
        }

        func controlTextDidChange(_ obj: Notification) {
            if let textField = obj.object as? NSTextField {
                parent.text = textField.stringValue
            }
        }

        func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
            if commandSelector == #selector(NSResponder.moveUp(_:)) {
                return parent.onKeyDown(NSEvent.keyEvent(with: .keyDown, location: .zero, modifierFlags: [], timestamp: 0, windowNumber: 0, context: nil, characters: "", charactersIgnoringModifiers: "", isARepeat: false, keyCode: 126)!)
            } else if commandSelector == #selector(NSResponder.moveDown(_:)) {
                return parent.onKeyDown(NSEvent.keyEvent(with: .keyDown, location: .zero, modifierFlags: [], timestamp: 0, windowNumber: 0, context: nil, characters: "", charactersIgnoringModifiers: "", isARepeat: false, keyCode: 125)!)
            } else if commandSelector == #selector(NSResponder.insertNewline(_:)) {
                return parent.onKeyDown(NSEvent.keyEvent(with: .keyDown, location: .zero, modifierFlags: [], timestamp: 0, windowNumber: 0, context: nil, characters: "\r", charactersIgnoringModifiers: "\r", isARepeat: false, keyCode: 36)!)
            }
            return false
        }
    }
}

enum KeyboardNavigationDirection {
    case up, down, enter
}


================================================
FILE: There/Views/Timezone/AddTimezone + Functions.swift
================================================
import AppKit
import CoreLocation
import Foundation
import MapKit
import SwiftUI

extension AddTimezone {
    func searchPlace(_ completion: MKLocalSearchCompletion) {
        let searchRequest = MKLocalSearch.Request(completion: completion)
        let search = MKLocalSearch(request: searchRequest)
        search.start { response, _ in
            guard let coordinate = response?.mapItems.first?.placemark.coordinate else { return }

            let location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
            CLGeocoder().reverseGeocodeLocation(location) { placemarks, _ in
                DispatchQueue.main.async {
                    if let placemark = placemarks?.first {
                        if let timeZone = placemark.timeZone {
                            self.selectedTimeZone = timeZone
                        }
                        self.countryEmoji = Utils.shared.getCountryEmoji(for: placemark.isoCountryCode ?? "")
                    }
                }
            }
        }
    }

    func saveEntry() {
        let fileName = UUID().uuidString + ".png"
        let fileURL = getApplicationSupportDirectory().appendingPathComponent(fileName)

        if let tiffData = image?.tiffRepresentation,
           let bitmapImage = NSBitmapImageRep(data: tiffData),
           let pngData = bitmapImage.representation(using: .png, properties: [:]) {
            do {
                try pngData.write(to: fileURL)
            } catch {
                print("Failed to save image: \(error)")
            }
        }

        do {
            try database.dbWriter.write { db in
                let entry = Entry(
                    id: Int64.random(in: 1 ... 99999),
                    type: !countryEmoji.isEmpty && image == nil ? .place : .person,
                    name: name,
                    city: city,
                    timezoneIdentifier: selectedTimeZone?.identifier ?? "",
                    flag: image == nil ? countryEmoji : "",
                    photoData: image != nil ? fileURL.absoluteString : nil
                )

                try entry.save(db)
            }
        } catch {
            print("Failed to save entry \(error)")
        }

        router.cleanActiveRoute()
        resetForm()
    }

    private func resetForm() {
        image = nil
        name = ""
        city = ""
        showingXAccountInput = false
        showingTGAccountInput = false
        selectedTimeZone = TimeZone.current
        isShowingPopover = false
        countryEmoji = ""
        selectedTimeZone = nil
    }

    private func getApplicationSupportDirectory() -> URL {
        FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
    }
}


================================================
FILE: There/Views/Timezone/AddTimezone.swift
================================================
import CoreLocation
import MapKit
import SwiftUI

struct AddTimezone: View {
    @Environment(\.database) var database
    @StateObject private var searchCompleter = SearchCompleter()

    @EnvironmentObject var router: Router

    @State var image: NSImage?
    @State var name = ""
    @State var city = ""
    @State var selectedTimeZone: TimeZone? = nil
    @State var isShowingPopover = false
    @State var countryEmoji = ""

    @State var showingXAccountInput = false
    @State var showingTGAccountInput = false

    var body: some View {
        VStack(alignment: .center, spacing: 0) {
            IconSection(
                image: $image,
                countryEmoji: $countryEmoji,
                showingXAccountInput: $showingXAccountInput,
                showingTGAccountInput: $showingTGAccountInput
            )

            FormSection(
                name: $name,
                city: $city,
                selectedTimeZone: $selectedTimeZone,
                isShowingPopover: $isShowingPopover,
                searchCompleter: searchCompleter,
                countryEmoji: $countryEmoji,
                image: $image,
                showingTGAccountInput: $showingTGAccountInput,
                showingXAccountInput: $showingXAccountInput,
                saveEntry: saveEntry
            )
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)

        .overlay(alignment: .topLeading) {
            Titlebar()
                .padding(6)
        }
    }
}

#Preview {
    AddTimezone()
        .frame(width: 300, height: 400)
}


================================================
FILE: There/Views/Timezone/EditTimeZone + Functions.swift
================================================
import AppKit
import CoreLocation
import Foundation
import MapKit
import SwiftUI

extension EditTimeZoneView {
    func saveEntry() {
        let fileName = UUID().uuidString + ".png"
        let fileURL = getApplicationSupportDirectory().appendingPathComponent(fileName)

        if let tiffData = image?.tiffRepresentation,
           let bitmapImage = NSBitmapImageRep(data: tiffData),
           let pngData = bitmapImage.representation(using: .png, properties: [:]) {
            do {
                try pngData.write(to: fileURL)
            } catch {
                print("Failed to save image: \(error)")
            }
        }

        do {
            try database.dbWriter.write { db in
                if let entry = entry {
                    let entry = Entry(
                        id: entry.id,
                        type: !countryEmoji.isEmpty && image == nil ? .place : .person,
                        name: name,
                        city: city,
                        timezoneIdentifier: selectedTimeZone?.identifier ?? "",
                        flag: image == nil ? countryEmoji : "",
                        photoData: fileURL.absoluteString
                    )
                    try entry.save(db)
                }
            }
        } catch {
            print("Failed to save entry \(error)")
        }

        router.cleanActiveRoute()
    }

    private func getApplicationSupportDirectory() -> URL {
        FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
    }
}


================================================
FILE: There/Views/Timezone/EditTimeZoneView.swift
================================================
import CoreLocation
import MapKit
import SwiftUI

struct EditTimeZoneView: View {
    var entryId: Int64?
    @Environment(\.database) var database
    @StateObject private var searchCompleter = SearchCompleter()
    @EnvironmentObject var router: Router

    @State var entry: Entry?
    @State var image: NSImage?
    @State var name = ""
    @State var city = ""
    @State var selectedTimeZone: TimeZone?
    @State var isShowingPopover = false
    @State var countryEmoji = ""

    @State var showingXAccountInput = false
    @State var showingTGAccountInput = false

    @State var isLoading = true
    @State var errorMessage: String?

    var body: some View {
        VStack(alignment: .center, spacing: 0) {
            if isLoading {
                ProgressView()
            } else if let entry = entry {
                IconSection(
                    image: $image,
                    countryEmoji: $countryEmoji,
                    showingXAccountInput: $showingXAccountInput,
                    showingTGAccountInput: $showingTGAccountInput
                )

                FormSection(
                    name: $name,
                    city: $city,
                    selectedTimeZone: $selectedTimeZone,
                    isShowingPopover: $isShowingPopover,
                    searchCompleter: searchCompleter,
                    countryEmoji: $countryEmoji,
                    image: $image,
                    showingTGAccountInput: $showingTGAccountInput,
                    showingXAccountInput: $showingXAccountInput,
                    isEditing: true,
                    saveEntry: saveEntry
                )
            } else {
                NotFoundView()
            }

            if let errorMessage = errorMessage {
                Text(errorMessage)
                    .foregroundColor(.red)
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .overlay(alignment: .topLeading) {
            Titlebar()
                .padding(6)
        }
        .task {
            if entry == nil {
                await loadEntry()
            }
        }
    }

    private func loadEntry() async {
        isLoading = true
        do {
            try await database.reader.read { db in
                if let id = entryId {
                    let fetchedEntry = try Entry.fetchOne(db, id: id)
                    self.entry = fetchedEntry
                    if let entry = fetchedEntry {
                        self.name = entry.name
                        self.city = entry.city
                        self.selectedTimeZone = TimeZone(identifier: entry.timezoneIdentifier)
                        self.countryEmoji = entry.flag ?? ""
                        if entry.photoData != nil, let imageURL = URL(string: entry.photoData!) {
                            if let imageData = try? Data(contentsOf: imageURL) {
                                self.image = NSImage(data: imageData)
                            } else {
                                print("Failed to load image data from URL: \(imageURL)")
                            }
                        } else {
                            self.image = nil
                        }
                    }
                }
            }
        } catch {
            errorMessage = "Failed to load entry: \(error.localizedDescription)"
        }
        isLoading = false
    }
}

#Preview {
    EditTimeZoneView(entryId: 1712)
        .frame(width: 300, height: 400)
        .environment(\.database, .shared)
}


================================================
FILE: There/Views/Timezone/FormSection.swift
================================================
import PostHog
import SwiftUI
import UserNotifications

struct FormSection: View {
    @Binding var name: String
    @Binding var city: String
    @Binding var selectedTimeZone: TimeZone?
    @Binding var isShowingPopover: Bool
    @StateObject var searchCompleter: SearchCompleter
    @Binding var countryEmoji: String
    @Binding var image: NSImage?
    @Binding var showingTGAccountInput: Bool
    @Binding var showingXAccountInput: Bool
    @State var showError: Bool = false
    @State private var username = ""
    @State private var debounceTask: Task<Void, Never>?
    var isEditing: Bool = false
    let saveEntry: () -> Void

    var body: some View {
        VStack(alignment: .leading, spacing: 2) {
            StyledLabel(title: "Name")
            Input(text: $name, placeholder: "eg. Dena or London Office")
                .padding(.bottom, 6)
                .onSubmit {
                    if !city.isEmpty {
                        PostHogSDK.shared.capture("timezone_added")
                        saveEntry()
                    } else {
                        withAnimation(.easeIn(duration: 0.1)) {
                            showError = true
                        }
                    }
                }
            if !city.isEmpty {
                SecondaryButton(title: city) {
                    withAnimation(.easeOut(duration: 0.1)) {
                        showError = false
                    }
                    isShowingPopover = true
                }
                .popover(isPresented: $isShowingPopover) {
                    CitySearchResults(
                        searchCompleter: searchCompleter,
                        isShowingPopover: $isShowingPopover,
                        selectedCity: $city,
                        selectedTimezone: $selectedTimeZone,
                        countryEmoji: $countryEmoji
                    )
                }
            } else {
                SecondaryButton(title: "Set location / timezone") {
                    withAnimation(.easeOut(duration: 0.1)) {
                        showError = false
                    }
                    isShowingPopover = true
                }
                .popover(isPresented: $isShowingPopover) {
                    CitySearchResults(
                        searchCompleter: searchCompleter,
                        isShowingPopover: $isShowingPopover,
                        selectedCity: $city,
                        selectedTimezone: $selectedTimeZone,
                        countryEmoji: $countryEmoji
                    )
                }
            }

            if showError {
                Text("please select a Location")
                    .font(.caption)
                    .foregroundColor(.red)
                    .fontWeight(.medium)
                    .transition(.opacity)
            }

            PrimaryButton(title: isEditing ? "Update" : "Add", action: {
                if !city.isEmpty {
                    saveEntry()
                    PostHogSDK.shared.capture("timezone_added")
                } else {
                    withAnimation(.easeIn(duration: 0.1)) {
                        showError = true
                    }
                }

            })
            .disabled(city.isEmpty)
            .opacity(city.isEmpty ? 0.6 : 1)
            .padding(.top, 8)
        }
    }
}


================================================
FILE: There/Views/Timezone/IconSection.swift
================================================
import SwiftUI

struct IconSection: View {
    @Binding var image: NSImage?
    @Binding var countryEmoji: String
    @Binding var showingXAccountInput: Bool
    @Binding var showingTGAccountInput: Bool

    @State private var xHovered = false
    @State private var tgHovered = false

    var body: some View {
        VStack {
            IconView(
                image: $image,
                countryEmoji: $countryEmoji
            )
            .padding(.bottom, 6)
        }
    }
}

struct SocialMediaButton: View {
    let imageName: String
    @Binding var isHovered: Bool
    let action: () -> Void

    var body: some View {
        Button(action: action) {
            Image(imageName)
                .resizable()
                .frame(width: 18, height: 18)
                .clipShape(Circle())
        }
        .buttonStyle(PlainButtonStyle())
        .scaleEffect(isHovered ? 1.1 : 1)
        .shadow(color: isHovered ? .black.opacity(0.2) : .clear, radius: 4, x: 0, y: 4)
        .onHover { hovering in
            withAnimation {
                isHovered = hovering
            }
        }
    }
}

struct SocialMediaInput: View {
    let platform: String
    @Binding var username: String
    @Binding var image: NSImage?
    @Binding var debounceTask: Task<Void, Never>?

    var body: some View {
        VStack(alignment: .leading) {
            StyledLabel(title: platform == "X" ? "Enter an \(platform) username" : "Enter a \(platform) username")
                .padding(.top, 8)
            Input(text: $username, placeholder: "eg. dena_sohrabi")
                .onChange(of: username) { value in
                    debounceTask?.cancel()

                    if !value.isEmpty {
                        debounceTask = Task {
                            try? await Task.sleep(for: .milliseconds(800))

                            if !Task.isCancelled {
                                do {
                                    let imageUrl = "https://unavatar.io/\(platform.lowercased())/\(value.lowercased())"
                                    let fetchedImage = try await simpleImageFetch(from: imageUrl)
                                    await MainActor.run {
                                        self.image = NSImage(data: fetchedImage)
                                    }
                                } catch {
                                    print("Got error \(error)")
                                }
                            }
                        }
                    } else {
                        image = nil
                    }
                }
        }
    }

    func simpleImageFetch(from urlString: String) async throws -> Data {
        guard let url = URL(string: urlString) else {
            throw URLError(.badURL)
        }

        let (data, response) = try await URLSession.shared.data(from: url)

        guard let httpResponse = response as? HTTPURLResponse else {
            throw URLError(.badServerResponse)
        }

        image = NSImage(data: data)
        return data
    }
}


================================================
FILE: There/Views/Timezone/NotFoundView.swift
================================================
import SwiftUI

struct NotFoundView: View {
    var body: some View {
        VStack(spacing: 2) {
            Text("😕")
                .font(.largeTitle)
            Text("Entry not found")
                .font(.title)
                .fontWeight(.medium)
        }
    }
}

#Preview {
    NotFoundView()
        .frame(width: 320, height: 320)
}


================================================
FILE: There/pm.there.There.LaunchAgent.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>pm.there.There.LaunchAgent</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Applications/There.app/Contents/MacOS/There</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>LimitLoadToSessionType</key>
    <string>Aqua</string>
</dict>
</plist>


================================================
FILE: ThereTests/ThereTests.swift
================================================
//
//  ThereTests.swift
//  ThereTests
//
//  Created by Dena Sohrabi on 9/2/24.
//

import Testing

struct ThereTests {

    @Test func example() async throws {
        // Write your test here and use APIs like `#expect(...)` to check expected conditions.
    }

}



================================================
FILE: ThereUITests/ThereUITests.swift
================================================
//  ThereUITests.swift
//  ThereUITests
//
//  Created by Dena Sohrabi on 9/2/24.
//

import XCTest

final class ThereUITests: XCTestCase {
    let app = XCUIApplication()

    override func setUpWithError() throws {
        continueAfterFailure = false
        app.launch()
    }

    override func tearDownWithError() throws {
        // Terminate the app after each test
        app.terminate()
    }

    func printAccessibleElements() {
        let allElements = app.descendants(matching: .any)
        for element in allElements.allElementsBoundByIndex {
            print("Element: \(element.debugDescription)")
        }
    }

    @MainActor
    func testUIElementsExistence() throws {
        // Print all accessible elements
        print("Printing all accessible elements:")
        printAccessibleElements()

        // Check for specific element types
        print("\nSearching for specific element types:")
        let searchFields = app.searchFields.allElementsBoundByIndex
        print("Search Fields: \(searchFields.map { $0.debugDescription })")

        let textFields = app.textFields.allElementsBoundByIndex
        print("Text Fields: \(textFields.map { $0.debugDescription })")

        let buttons = app.buttons.allElementsBoundByIndex
        print("Buttons: \(buttons.map { $0.debugDescription })")

        let tables = app.tables.allElementsBoundByIndex
        print("Tables: \(tables.map { $0.debugDescription })")

        // Try to find any input field
        let possibleInputFields = app.descendants(matching: .any).matching(NSPredicate(format: "type == 'XCUIElementTypeTextField' OR type == 'XCUIElementTypeSearchField'"))
        print("\nPossible Input Fields:")
        for field in possibleInputFields.allElementsBoundByIndex {
            print(field.debugDescription)
        }

        // Assert that we found at least one possible input field
        XCTAssertTrue(possibleInputFields.count > 0, "No input fields found in the app")
    }

    @MainActor
    func testLaunchPerformance() throws {
        if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
            measure(metrics: [XCTApplicationLaunchMetric()]) {
                XCUIApplication().launch()
            }
        }
    }
}


================================================
FILE: ThereUITests/ThereUITestsLaunchTests.swift
================================================
//
//  ThereUITestsLaunchTests.swift
//  ThereUITests
//
//  Created by Dena Sohrabi on 9/2/24.
//

import XCTest

final class ThereUITestsLaunchTests: 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: readme.md
================================================
# [There](https://there.pm)

[![Swift](https://img.shields.io/badge/Swift-F54A2A?logo=swift&logoColor=white)](#) [![macOS](https://img.shields.io/badge/macOS-000000?logo=apple&logoColor=F0F0F0)](#)

A native menubar app to track friends, teammates or city time zones on macOS.

```
brew install --cask there
```

![Screen-shot of the app](https://there.pm/app@2x.jpg)

- Add photos for people from X (Twitter), Telegram or pick locally
- Add any city without knowing the time zone
- Supports raw UTC offsets
- 0-1% idle CPU usage
- Ultra-low memory
- Written in Swift UI
- macOS 13+

## Roadmap

We want to keep the app as simple as possible. But here are the things we're considering for future versions:

- [ ] Widgets
- [ ] Time slider to view future/past times
- [ ] Auto-update
- [ ] Apple Script API for third-party integrations i.e. Raycast

## Contributions

Contributions are welcome! For simple fixes or improvements feel free to open a PR with the smallest change.

For features or improvements that add scope to the app or have user facing changes, please open an issue first to discuss your proposal.

If you want to tackle any of the items in the roadmap section, please also open an issue.

## License

This project is licensed under the [MIT License](LICENSE)
Download .txt
gitextract_iq1mvdbh/

├── .gitignore
├── LICENSE
├── There/
│   ├── Assets.xcassets/
│   │   ├── AccentColor.colorset/
│   │   │   └── Contents.json
│   │   ├── AppIcon.appiconset/
│   │   │   └── Contents.json
│   │   ├── Contents.json
│   │   ├── Earth.imageset/
│   │   │   └── Contents.json
│   │   ├── Logo.imageset/
│   │   │   └── Contents.json
│   │   ├── Night.imageset/
│   │   │   └── Contents.json
│   │   ├── appIcon.imageset/
│   │   │   └── Contents.json
│   │   ├── early-afternoon.imageset/
│   │   │   └── Contents.json
│   │   ├── early-evening.imageset/
│   │   │   └── Contents.json
│   │   ├── early-morning.imageset/
│   │   │   └── Contents.json
│   │   ├── evening.imageset/
│   │   │   └── Contents.json
│   │   ├── late-afternoon.imageset/
│   │   │   └── Contents.json
│   │   ├── late-morning.imageset/
│   │   │   └── Contents.json
│   │   ├── telegram-logo.imageset/
│   │   │   └── Contents.json
│   │   └── twitter.imageset/
│   │       └── Contents.json
│   ├── ContentView.swift
│   ├── Data/
│   │   ├── Database.swift
│   │   ├── Entry.swift
│   │   ├── Fetcher.swift
│   │   ├── LaunchAgent.swift
│   │   ├── Persistence.swift
│   │   ├── Router.swift
│   │   ├── SearchCompleter.swift
│   │   └── Utilities.swift
│   ├── Preview Content/
│   │   └── Preview Assets.xcassets/
│   │       └── Contents.json
│   ├── There.entitlements
│   ├── ThereApp.swift
│   ├── UI/
│   │   ├── Button.swift
│   │   ├── Heading.swift
│   │   ├── Input.swift
│   │   ├── Label.swift
│   │   ├── LocalImageView.swift
│   │   ├── Titlebar.swift
│   │   └── TransparentBackgroundView.swift
│   ├── Views/
│   │   ├── ButtomBar/
│   │   │   ├── AddButton.swift
│   │   │   ├── BottomBarView.swift
│   │   │   └── SettingsButton.swift
│   │   ├── CLAuthorizationStatus+Description.swift 
│   │   ├── EmptyTimezoneView.swift
│   │   ├── EntryUI/
│   │   │   ├── EntryIcon.swift
│   │   │   └── EntryRow.swift
│   │   ├── MainView.swift
│   │   ├── Onboarding/
│   │   │   ├── InitialView.swift
│   │   │   ├── LeftPanel.swift
│   │   │   └── RightPanel.swift
│   │   └── Timezone/
│   │       ├── AddTimezone + Components.swift
│   │       ├── AddTimezone + Functions.swift
│   │       ├── AddTimezone.swift
│   │       ├── EditTimeZone + Functions.swift
│   │       ├── EditTimeZoneView.swift
│   │       ├── FormSection.swift
│   │       ├── IconSection.swift
│   │       └── NotFoundView.swift
│   └── pm.there.There.LaunchAgent.plist
├── ThereTests/
│   └── ThereTests.swift
├── ThereUITests/
│   ├── ThereUITests.swift
│   └── ThereUITestsLaunchTests.swift
└── readme.md
Condensed preview — 60 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (123K chars).
[
  {
    "path": ".gitignore",
    "chars": 881,
    "preview": "# Xcode\n.DS_Store\nxcuserdata/\n*.xcodeproj/*\n!*.xcodeproj/project.pbxproj\n!*.xcodeproj/xcshareddata/\n!*.xcodeproj/project"
  },
  {
    "path": "LICENSE",
    "chars": 1121,
    "preview": "### MIT License\n\n```\nMIT License\n\nCopyright (c) 2024 Dena Sohrabi\n\nPermission is hereby granted, free of charge, to any "
  },
  {
    "path": "There/Assets.xcassets/AccentColor.colorset/Contents.json",
    "chars": 123,
    "preview": "{\n  \"colors\" : [\n    {\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }"
  },
  {
    "path": "There/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "chars": 1297,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"icon-166.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"1"
  },
  {
    "path": "There/Assets.xcassets/Contents.json",
    "chars": 63,
    "preview": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "There/Assets.xcassets/Earth.imageset/Contents.json",
    "chars": 307,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"filename\" : \"Earch&Sun.png\""
  },
  {
    "path": "There/Assets.xcassets/Logo.imageset/Contents.json",
    "chars": 307,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"filename\" : \"ThereIcon.png\""
  },
  {
    "path": "There/Assets.xcassets/Night.imageset/Contents.json",
    "chars": 303,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"Night.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n  "
  },
  {
    "path": "There/Assets.xcassets/appIcon.imageset/Contents.json",
    "chars": 463,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"iconTemplate.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n "
  },
  {
    "path": "There/Assets.xcassets/early-afternoon.imageset/Contents.json",
    "chars": 313,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"Early afternoon.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    }"
  },
  {
    "path": "There/Assets.xcassets/early-evening.imageset/Contents.json",
    "chars": 311,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"Early Eavning.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n"
  },
  {
    "path": "There/Assets.xcassets/early-morning.imageset/Contents.json",
    "chars": 311,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"Early Morning.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n"
  },
  {
    "path": "There/Assets.xcassets/evening.imageset/Contents.json",
    "chars": 305,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"Eavning.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n"
  },
  {
    "path": "There/Assets.xcassets/late-afternoon.imageset/Contents.json",
    "chars": 312,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"Late Afternoon.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },"
  },
  {
    "path": "There/Assets.xcassets/late-morning.imageset/Contents.json",
    "chars": 310,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"Late Morning.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n "
  },
  {
    "path": "There/Assets.xcassets/telegram-logo.imageset/Contents.json",
    "chars": 302,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"Logo.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n   "
  },
  {
    "path": "There/Assets.xcassets/twitter.imageset/Contents.json",
    "chars": 305,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"twitter.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n"
  },
  {
    "path": "There/ContentView.swift",
    "chars": 641,
    "preview": "import SwiftUI\n\nstruct ContentView: View {\n    @EnvironmentObject var router: Router\n    var body: some View {\n        s"
  },
  {
    "path": "There/Data/Database.swift",
    "chars": 5231,
    "preview": "import Foundation\nimport GRDB\nimport os.log\n\n///\n/// You create an `AppDatabase` with a connection to an SQLite database"
  },
  {
    "path": "There/Data/Entry.swift",
    "chars": 2863,
    "preview": "import Foundation\nimport GRDB\nimport SwiftUI\n\nenum EntryType: String, Codable {\n    case place\n    case person\n}\n\nenum D"
  },
  {
    "path": "There/Data/Fetcher.swift",
    "chars": 954,
    "preview": "import Combine\nimport Foundation\nimport GRDB\n\nenum SortOrder {\n    case timeAscending\n    case timeDescending\n}\n\nclass F"
  },
  {
    "path": "There/Data/LaunchAgent.swift",
    "chars": 3785,
    "preview": "import Foundation\nimport ServiceManagement\n\nfunc installLaunchAgent() {\n    let fileManager = FileManager.default\n\n    g"
  },
  {
    "path": "There/Data/Persistence.swift",
    "chars": 2926,
    "preview": "import Foundation\nimport GRDB\n\nextension AppDatabase {\n    /// The database for the application\n    static let shared = "
  },
  {
    "path": "There/Data/Router.swift",
    "chars": 438,
    "preview": "import SwiftUI\n\nenum Route {\n    case addTimezone\n    case mainView\n    case editTimeZone(entryId: Int64?)\n}\n\nclass Rout"
  },
  {
    "path": "There/Data/SearchCompleter.swift",
    "chars": 9392,
    "preview": "import Foundation\nimport MapKit\n\nclass TimeZoneSearchCompiler: NSObject {\n    private var commonAbbreviations: [String: "
  },
  {
    "path": "There/Data/Utilities.swift",
    "chars": 1060,
    "preview": "import Foundation\nimport SwiftUI\n\nclass Utils {\n    public static var shared = Utils()\n    func selectPhoto() -> NSImage"
  },
  {
    "path": "There/Preview Content/Preview Assets.xcassets/Contents.json",
    "chars": 63,
    "preview": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "There/There.entitlements",
    "chars": 645,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "There/ThereApp.swift",
    "chars": 3031,
    "preview": "//\n//  ThereApp.swift\n//  There\n//\n//  Created by Dena Sohrabi on 9/2/24.\n//\n\nimport AppKit\nimport MenuBarExtraAccess\nim"
  },
  {
    "path": "There/UI/Button.swift",
    "chars": 7004,
    "preview": "import SwiftUI\n\n// PrimaryButton\nstruct PrimaryButton: View {\n    var title: String\n    var action: () -> Void\n\n    var "
  },
  {
    "path": "There/UI/Heading.swift",
    "chars": 226,
    "preview": "import SwiftUI\n\nstruct Heading: View {\n    let title: String\n\n    var body: some View {\n        Text(title)\n            "
  },
  {
    "path": "There/UI/Input.swift",
    "chars": 3214,
    "preview": "import SwiftUI\n\nstruct AdaptiveColors {\n    static let textFieldBackground = Color(.textBackgroundColor)\n    static let "
  },
  {
    "path": "There/UI/Label.swift",
    "chars": 273,
    "preview": "import SwiftUI\n\nstruct StyledLabel: View {\n    let title: String\n    \n    var body: some View {\n        Text(title)\n    "
  },
  {
    "path": "There/UI/LocalImageView.swift",
    "chars": 1269,
    "preview": "import SwiftUI\n\nstruct LocalImageView: View {\n    let imageURL: URL\n\n    @State private var image: NSImage?\n    @State p"
  },
  {
    "path": "There/UI/Titlebar.swift",
    "chars": 1383,
    "preview": "import SwiftUI\n\nstruct Titlebar: View {\n    @EnvironmentObject var router: Router\n    @State var hovered: Bool = false\n "
  },
  {
    "path": "There/UI/TransparentBackgroundView.swift",
    "chars": 426,
    "preview": "import SwiftUI\n\nstruct TransparentBackgroundView: NSViewRepresentable {\n    func makeNSView(context: Context) -> NSVisua"
  },
  {
    "path": "There/Views/ButtomBar/AddButton.swift",
    "chars": 361,
    "preview": "import SwiftUI\n\nstruct AddButton: View {\n    @State private var addHovered: Bool = false\n    @EnvironmentObject var appS"
  },
  {
    "path": "There/Views/ButtomBar/BottomBarView.swift",
    "chars": 1073,
    "preview": "import SwiftUI\n\nstruct BottomBarView: View {\n    @Binding var isAtBottom: Bool\n    @Binding var sortOrder: SortOrder\n   "
  },
  {
    "path": "There/Views/ButtomBar/SettingsButton.swift",
    "chars": 5015,
    "preview": "import SwiftUI\n\nstruct SettingsButton: View {\n    @State private var settingsHovered: Bool = false\n    @Environment(\\.op"
  },
  {
    "path": "There/Views/CLAuthorizationStatus+Description.swift ",
    "chars": 396,
    "preview": "import CoreLocation\n\nextension CLAuthorizationStatus: CustomStringConvertible {\n    public var description: String {\n   "
  },
  {
    "path": "There/Views/EmptyTimezoneView.swift",
    "chars": 557,
    "preview": "import SwiftUI\n\nstruct EmptyTimezoneView: View {\n    var body: some View {\n        VStack {\n            Image(\"Earth\")\n "
  },
  {
    "path": "There/Views/EntryUI/EntryIcon.swift",
    "chars": 3947,
    "preview": "import CoreLocation\nimport SwiftUI\n\nstruct EntryIcon: View {\n    let entry: Entry\n    @Environment(\\.colorScheme) var sc"
  },
  {
    "path": "There/Views/EntryUI/EntryRow.swift",
    "chars": 4373,
    "preview": "import Combine\nimport SwiftUI\n\nstruct EntryRow: View {\n    let entry: Entry\n    @State private var isHovered: Bool = fal"
  },
  {
    "path": "There/Views/MainView.swift",
    "chars": 6480,
    "preview": "import AppKit\nimport GRDB\nimport PostHog\nimport SwiftUI\n\nstruct MainView: View {\n    @StateObject private var fetcher = "
  },
  {
    "path": "There/Views/Onboarding/InitialView.swift",
    "chars": 2131,
    "preview": "// InitialView.swift\n\nimport PostHog\nimport SwiftUI\n\nstruct InitialView: View {\n    @Environment(\\.database) var databas"
  },
  {
    "path": "There/Views/Onboarding/LeftPanel.swift",
    "chars": 1611,
    "preview": "// LeftPanel.swift\n\nimport SwiftUI\n\nstruct LeftPanel: View {\n    var body: some View {\n        VStack(alignment: .leadin"
  },
  {
    "path": "There/Views/Onboarding/RightPanel.swift",
    "chars": 1095,
    "preview": "// RightPanel.swift\n\nimport PostHog\nimport SwiftUI\n\nstruct RightPanel: View {\n    @Binding var email: String\n    let sav"
  },
  {
    "path": "There/Views/Timezone/AddTimezone + Components.swift",
    "chars": 13525,
    "preview": "import AppKit\nimport MapKit\nimport SwiftUI\n\n// MARK: - IconView\n\nstruct IconView: View {\n    @Binding var image: NSImage"
  },
  {
    "path": "There/Views/Timezone/AddTimezone + Functions.swift",
    "chars": 2760,
    "preview": "import AppKit\nimport CoreLocation\nimport Foundation\nimport MapKit\nimport SwiftUI\n\nextension AddTimezone {\n    func searc"
  },
  {
    "path": "There/Views/Timezone/AddTimezone.swift",
    "chars": 1579,
    "preview": "import CoreLocation\nimport MapKit\nimport SwiftUI\n\nstruct AddTimezone: View {\n    @Environment(\\.database) var database\n "
  },
  {
    "path": "There/Views/Timezone/EditTimeZone + Functions.swift",
    "chars": 1552,
    "preview": "import AppKit\nimport CoreLocation\nimport Foundation\nimport MapKit\nimport SwiftUI\n\nextension EditTimeZoneView {\n    func "
  },
  {
    "path": "There/Views/Timezone/EditTimeZoneView.swift",
    "chars": 3565,
    "preview": "import CoreLocation\nimport MapKit\nimport SwiftUI\n\nstruct EditTimeZoneView: View {\n    var entryId: Int64?\n    @Environme"
  },
  {
    "path": "There/Views/Timezone/FormSection.swift",
    "chars": 3391,
    "preview": "import PostHog\nimport SwiftUI\nimport UserNotifications\n\nstruct FormSection: View {\n    @Binding var name: String\n    @Bi"
  },
  {
    "path": "There/Views/Timezone/IconSection.swift",
    "chars": 3077,
    "preview": "import SwiftUI\n\nstruct IconSection: View {\n    @Binding var image: NSImage?\n    @Binding var countryEmoji: String\n    @B"
  },
  {
    "path": "There/Views/Timezone/NotFoundView.swift",
    "chars": 350,
    "preview": "import SwiftUI\n\nstruct NotFoundView: View {\n    var body: some View {\n        VStack(spacing: 2) {\n            Text(\"😕\")"
  },
  {
    "path": "There/pm.there.There.LaunchAgent.plist",
    "chars": 522,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "ThereTests/ThereTests.swift",
    "chars": 267,
    "preview": "//\n//  ThereTests.swift\n//  ThereTests\n//\n//  Created by Dena Sohrabi on 9/2/24.\n//\n\nimport Testing\n\nstruct ThereTests {"
  },
  {
    "path": "ThereUITests/ThereUITests.swift",
    "chars": 2255,
    "preview": "//  ThereUITests.swift\n//  ThereUITests\n//\n//  Created by Dena Sohrabi on 9/2/24.\n//\n\nimport XCTest\n\nfinal class ThereUI"
  },
  {
    "path": "ThereUITests/ThereUITestsLaunchTests.swift",
    "chars": 810,
    "preview": "//\n//  ThereUITestsLaunchTests.swift\n//  ThereUITests\n//\n//  Created by Dena Sohrabi on 9/2/24.\n//\n\nimport XCTest\n\nfinal"
  },
  {
    "path": "readme.md",
    "chars": 1276,
    "preview": "# [There](https://there.pm)\n\n[![Swift](https://img.shields.io/badge/Swift-F54A2A?logo=swift&logoColor=white)](#) [![macO"
  }
]

About this extraction

This page contains the full source code of the dena-sohrabi/There GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 60 files (111.5 KB), approximately 27.5k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!