diff --git a/.github/workflows/native.yml b/.github/workflows/native.yml new file mode 100644 index 0000000..f65bef2 --- /dev/null +++ b/.github/workflows/native.yml @@ -0,0 +1,41 @@ +--- +name: Native macOS + +on: + push: + branches: + - '**' + paths: + - 'apps/native-macos/**' + - '.github/workflows/native.yml' + +permissions: + contents: read + +jobs: + lint: + runs-on: macos-26 + defaults: + run: + working-directory: apps/native-macos + steps: + - uses: actions/checkout@v7 + - name: Select Xcode 26 + run: sudo xcode-select -s /Applications/Xcode_26.app 2>/dev/null || sudo xcode-select -s /Applications/Xcode.app + - name: SwiftFormat (lint) + run: swift package --allow-writing-to-package-directory swiftformat --lint + - name: SwiftLint + run: swift package --allow-writing-to-package-directory swiftlint --strict + build-and-test: + runs-on: macos-26 + defaults: + run: + working-directory: apps/native-macos + steps: + - uses: actions/checkout@v7 + - name: Select Xcode 26 + run: sudo xcode-select -s /Applications/Xcode_26.app 2>/dev/null || sudo xcode-select -s /Applications/Xcode.app + - name: swift build + run: swift build + - name: swift test + run: swift test diff --git a/.gitignore b/.gitignore index 28fade4..c91f9cf 100644 --- a/.gitignore +++ b/.gitignore @@ -175,3 +175,8 @@ docs/public/rustdoc/ test-results/** playwright/screenshots/** + +# Swift Package Manager build output +apps/native-macos/.build/ +apps/native-macos/.swiftpm/ +apps/native-macos/*.xcodeproj diff --git a/CLAUDE.md b/CLAUDE.md index d1c12e4..eb1bb98 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co MC-Vector is a cross-platform desktop app (Tauri v2 + React 19 + TypeScript) for Minecraft server management. It handles server lifecycle, real-time monitoring, plugin/mod browsing (Modrinth, Hangar, SpigotMC), file editing (Monaco Editor), backups, Java version management, and Ngrok integration. -A macOS Tahoe (26)+ native app (Swift 6 / SwiftUI / AppKit) is planned as an independent reimplementation — not a shared-core port. See `spec/native-macos-requirements.md` for the full rationale. +A macOS Tahoe (26)+ native app (Swift 6 / SwiftUI / AppKit) is planned as an independent reimplementation — not a shared-core port. See `spec/native-macos-requirements.md` for the full rationale. The SwiftPM package lives under `apps/native-macos/` — see `apps/native-macos/README.md` for build/lint commands. After any refactor: run `pnpm build` and confirm it succeeds before finishing. diff --git a/apps/native-macos/.swiftformat b/apps/native-macos/.swiftformat new file mode 100644 index 0000000..b407da3 --- /dev/null +++ b/apps/native-macos/.swiftformat @@ -0,0 +1,7 @@ +--swiftversion 6.2 +--indent 4 +--maxwidth 120 +--self insert +--importgrouping testable-last +--exclude .build +--disable swiftTestingTestCaseNames diff --git a/apps/native-macos/.swiftlint.yml b/apps/native-macos/.swiftlint.yml new file mode 100644 index 0000000..f876410 --- /dev/null +++ b/apps/native-macos/.swiftlint.yml @@ -0,0 +1,14 @@ +strict: true +reporter: "xcode" + +excluded: + - .build + +opt_in_rules: + - empty_count + - explicit_init + - fatal_error_message + - force_unwrapping + +disabled_rules: + - todo diff --git a/apps/native-macos/Package.resolved b/apps/native-macos/Package.resolved new file mode 100644 index 0000000..9f89549 --- /dev/null +++ b/apps/native-macos/Package.resolved @@ -0,0 +1,105 @@ +{ + "originHash" : "379e58a7245678ab1e3c48994f3dc224133ae4598cf751f783f3d5c657ead955", + "pins" : [ + { + "identity" : "collectionconcurrencykit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/JohnSundell/CollectionConcurrencyKit.git", + "state" : { + "revision" : "b4f23e24b5a1bff301efc5e70871083ca029ff95", + "version" : "0.2.0" + } + }, + { + "identity" : "cryptoswift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/krzyzanowskim/CryptoSwift.git", + "state" : { + "revision" : "f2a627b84c1ff96f21ac2fcb623ab36142dd5512", + "version" : "1.10.0" + } + }, + { + "identity" : "sourcekitten", + "kind" : "remoteSourceControl", + "location" : "https://github.com/jpsim/SourceKitten.git", + "state" : { + "revision" : "6529c17fe80dd94843a3df7ed3e6a239790d5c91", + "version" : "0.37.3" + } + }, + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser.git", + "state" : { + "revision" : "6a52f3251125d74daf04fcbd5e6f08a75d074382", + "version" : "1.8.2" + } + }, + { + "identity" : "swift-filename-matcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ileitch/swift-filename-matcher", + "state" : { + "revision" : "eef5ac0b6b3cdc64b3039b037bed2def8a1edaeb", + "version" : "2.0.1" + } + }, + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-syntax.git", + "state" : { + "revision" : "a8b1c535647243d603b6772e7e8306c993a0c188", + "version" : "605.0.0-prerelease-2026-06-26" + } + }, + { + "identity" : "swiftformat", + "kind" : "remoteSourceControl", + "location" : "https://github.com/nicklockwood/SwiftFormat", + "state" : { + "revision" : "2226e9d89bf05a604cc985bab3dccb44ff7c8ee3", + "version" : "0.62.1" + } + }, + { + "identity" : "swiftlint", + "kind" : "remoteSourceControl", + "location" : "https://github.com/realm/SwiftLint", + "state" : { + "revision" : "fd768ba9a0e8a4f96d550d98de6c4cf2af565cf1", + "version" : "0.65.0" + } + }, + { + "identity" : "swiftytexttable", + "kind" : "remoteSourceControl", + "location" : "https://github.com/scottrhoyt/SwiftyTextTable.git", + "state" : { + "revision" : "c6df6cf533d120716bff38f8ff9885e1ce2a4ac3", + "version" : "0.9.0" + } + }, + { + "identity" : "swxmlhash", + "kind" : "remoteSourceControl", + "location" : "https://github.com/drmohundro/SWXMLHash.git", + "state" : { + "revision" : "a853604c9e9a83ad9954c7e3d2a565273982471f", + "version" : "7.0.2" + } + }, + { + "identity" : "yams", + "kind" : "remoteSourceControl", + "location" : "https://github.com/jpsim/Yams.git", + "state" : { + "revision" : "a27b21e0c81c5bf42049b897a62aaf387e80f279", + "version" : "6.2.2" + } + } + ], + "version" : 3 +} diff --git a/apps/native-macos/Package.swift b/apps/native-macos/Package.swift new file mode 100644 index 0000000..6343fc2 --- /dev/null +++ b/apps/native-macos/Package.swift @@ -0,0 +1,36 @@ +// swift-tools-version:6.2 +import PackageDescription + +let package = Package( + name: "Native", + platforms: [.macOS(.v26)], + products: [ + .library(name: "Core", targets: ["Core"]) + ], + dependencies: [ + .package(url: "https://github.com/nicklockwood/SwiftFormat", from: "0.62.1"), + .package(url: "https://github.com/realm/SwiftLint", from: "0.65.0") + ], + targets: [ + .executableTarget( + name: "App", + dependencies: ["Core"], + plugins: [ + .plugin(name: "SwiftLintBuildToolPlugin", package: "SwiftLint") + ] + ), + .target( + name: "Core", + plugins: [ + .plugin(name: "SwiftLintBuildToolPlugin", package: "SwiftLint") + ] + ), + .testTarget( + name: "CoreTests", + dependencies: ["Core"], + plugins: [ + .plugin(name: "SwiftLintBuildToolPlugin", package: "SwiftLint") + ] + ) + ] +) diff --git a/apps/native-macos/README.md b/apps/native-macos/README.md new file mode 100644 index 0000000..1756692 --- /dev/null +++ b/apps/native-macos/README.md @@ -0,0 +1,44 @@ +# MC-Vector Native (macOS) + +Independent Swift/SwiftUI reimplementation of MC-Vector for macOS Tahoe (26)+. +See `spec/native-macos-requirements.md` for why this isn't a shared-core port, +and `spec/phase-tasks.md` for the phase-by-phase task breakdown. + +No `.xcodeproj` — this is a plain SwiftPM package. `App` is a thin +`@main` entry point; `Core` holds the actual views/logic (this split is what +lets `#Preview` work without `ENABLE_DEBUG_DYLIB`, which can only be set via +an `.xcodeproj`). + +## Requirements + +- macOS 26 (Tahoe)+ +- Xcode 26+ / Swift 6.2+ + +## Commands + +Run from `apps/native-macos/`: + +```bash +swift build # build App + Core +swift test # run CoreTests (Swift Testing) +``` + +Format and lint are exposed as SwiftPM plugins: + +```bash +# SwiftFormat (command plugin — no build-tool plugin exists upstream) +swift package --allow-writing-to-package-directory swiftformat # write +swift package --allow-writing-to-package-directory swiftformat --lint # check only + +# SwiftLint (build-tool plugin — runs automatically as part of `swift build`/`swift test`) +swift package --allow-writing-to-package-directory swiftlint --fix # autocorrect +``` + +`lefthook` runs both automatically on commit for files under `apps/native-macos/**/*.swift` +(see the `lint-format-swift` job in the root `lefthook.yml`); it no-ops with a message if +`swift` isn't on `PATH` locally, since CI (`.github/workflows/native.yml`) is the enforcement +backstop either way. + +## Opening in Xcode + +Xcode 26 can open `Package.swift` directly — no separate project file needed. diff --git a/apps/native-macos/Sources/App/Main.swift b/apps/native-macos/Sources/App/Main.swift new file mode 100644 index 0000000..6f311bf --- /dev/null +++ b/apps/native-macos/Sources/App/Main.swift @@ -0,0 +1,8 @@ +import Core + +@main +struct Main { + static func main() { + print("MC-Vector Native starting…") + } +} diff --git a/apps/native-macos/Sources/Core/Core.swift b/apps/native-macos/Sources/Core/Core.swift new file mode 100644 index 0000000..9e17e02 --- /dev/null +++ b/apps/native-macos/Sources/Core/Core.swift @@ -0,0 +1,3 @@ +public enum Core { + public static let version = "0.0.1" +} diff --git a/apps/native-macos/Sources/Core/RootView.swift b/apps/native-macos/Sources/Core/RootView.swift new file mode 100644 index 0000000..d74db5e --- /dev/null +++ b/apps/native-macos/Sources/Core/RootView.swift @@ -0,0 +1,15 @@ +import SwiftUI + +public struct RootView: View { + public init() {} + + public var body: some View { + Text("MC-Vector Native") + .font(.title) + .padding() + } +} + +#Preview { + RootView() +} diff --git a/apps/native-macos/Tests/CoreTests/CoreTests.swift b/apps/native-macos/Tests/CoreTests/CoreTests.swift new file mode 100644 index 0000000..21e44bc --- /dev/null +++ b/apps/native-macos/Tests/CoreTests/CoreTests.swift @@ -0,0 +1,7 @@ +import Testing +@testable import Core + +@Test("Core.version is not empty") +func coreVersionIsSet() { + #expect(!Core.version.isEmpty) +} diff --git a/lefthook.yml b/lefthook.yml index 289558d..5dec7c5 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -12,3 +12,14 @@ pre-commit: glob: 'docs/src/**/*.{ts,mjs}' run: pnpm --filter @mc-vector/docs check:fix stage_fixed: true + lint-format-swift: + glob: 'apps/native-macos/**/*.swift' + run: | + if ! command -v swift >/dev/null 2>&1; then + echo "swift not found locally, skipping Swift format/lint (still enforced in CI)" + exit 0 + fi + cd apps/native-macos && \ + swift package --allow-writing-to-package-directory swiftformat && \ + swift package --allow-writing-to-package-directory swiftlint --fix + stage_fixed: true