Bläddra i källkod

Add initial Gramora macOS app with Grammar Checker layout.

Scaffold the SwiftUI app to match the Grammo design, including sidebar navigation, premium card, and grammar checker screen.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 1 månad sedan
incheckning
73af5d5972

+ 5 - 0
.gitignore

@@ -0,0 +1,5 @@
+.DS_Store
+*.xcuserstate
+xcuserdata/
+DerivedData/
+build/

+ 330 - 0
gramora.xcodeproj/project.pbxproj

@@ -0,0 +1,330 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 77;
+	objects = {
+
+/* Begin PBXFileReference section */
+		A10000012F00000000000001 /* gramora.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = gramora.app; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFileSystemSynchronizedRootGroup section */
+		A10000022F00000000000001 /* gramora */ = {
+			isa = PBXFileSystemSynchronizedRootGroup;
+			path = gramora;
+			sourceTree = "<group>";
+		};
+/* End PBXFileSystemSynchronizedRootGroup section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		A10000032F00000000000001 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		A10000042F00000000000001 = {
+			isa = PBXGroup;
+			children = (
+				A10000022F00000000000001 /* gramora */,
+				A10000052F00000000000001 /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		A10000052F00000000000001 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				A10000012F00000000000001 /* gramora.app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		A10000062F00000000000001 /* gramora */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = A10000072F00000000000001 /* Build configuration list for PBXNativeTarget "gramora" */;
+			buildPhases = (
+				A10000082F00000000000001 /* Sources */,
+				A10000032F00000000000001 /* Frameworks */,
+				A10000092F00000000000001 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			fileSystemSynchronizedGroups = (
+				A10000022F00000000000001 /* gramora */,
+			);
+			name = gramora;
+			packageProductDependencies = (
+			);
+			productName = gramora;
+			productReference = A10000012F00000000000001 /* gramora.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		A100000A2F00000000000001 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				BuildIndependentTargetsInParallel = 1;
+				LastSwiftUpdateCheck = 2640;
+				LastUpgradeCheck = 2640;
+				TargetAttributes = {
+					A10000062F00000000000001 = {
+						CreatedOnToolsVersion = 26.4.1;
+					};
+				};
+			};
+			buildConfigurationList = A100000B2F00000000000001 /* Build configuration list for PBXProject "gramora" */;
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = A10000042F00000000000001;
+			minimizedProjectReferenceProxies = 1;
+			preferredProjectObjectVersion = 77;
+			productRefGroup = A10000052F00000000000001 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				A10000062F00000000000001 /* gramora */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		A10000092F00000000000001 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		A10000082F00000000000001 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		A100000C2F00000000000001 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				ENABLE_USER_SCRIPT_SANDBOXING = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu17;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+				MACOSX_DEPLOYMENT_TARGET = 14.0;
+				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+				MTL_FAST_MATH = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = macosx;
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+			};
+			name = Debug;
+		};
+		A100000D2F00000000000001 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_USER_SCRIPT_SANDBOXING = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu17;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+				MACOSX_DEPLOYMENT_TARGET = 14.0;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				MTL_FAST_MATH = YES;
+				SDKROOT = macosx;
+				SWIFT_COMPILATION_MODE = wholemodule;
+			};
+			name = Release;
+		};
+		A100000E2F00000000000001 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CODE_SIGN_ENTITLEMENTS = gramora/gramora.entitlements;
+				CODE_SIGN_STYLE = Automatic;
+				COMBINE_HIDPI_IMAGES = YES;
+				CURRENT_PROJECT_VERSION = 1;
+				ENABLE_APP_SANDBOX = YES;
+				ENABLE_USER_SELECTED_FILES = readonly;
+				GENERATE_INFOPLIST_FILE = YES;
+				INFOPLIST_FILE = gramora/Info.plist;
+				INFOPLIST_KEY_NSHumanReadableCopyright = "";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/../Frameworks",
+				);
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = "MQL-DEV.gramora";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				STRING_CATALOG_GENERATE_SYMBOLS = YES;
+				SWIFT_APPROACHABLE_CONCURRENCY = YES;
+				SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
+				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
+				SWIFT_VERSION = 5.0;
+			};
+			name = Debug;
+		};
+		A100000F2F00000000000001 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CODE_SIGN_ENTITLEMENTS = gramora/gramora.entitlements;
+				CODE_SIGN_STYLE = Automatic;
+				COMBINE_HIDPI_IMAGES = YES;
+				CURRENT_PROJECT_VERSION = 1;
+				ENABLE_APP_SANDBOX = YES;
+				ENABLE_USER_SELECTED_FILES = readonly;
+				GENERATE_INFOPLIST_FILE = YES;
+				INFOPLIST_FILE = gramora/Info.plist;
+				INFOPLIST_KEY_NSHumanReadableCopyright = "";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/../Frameworks",
+				);
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = "MQL-DEV.gramora";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				STRING_CATALOG_GENERATE_SYMBOLS = YES;
+				SWIFT_APPROACHABLE_CONCURRENCY = YES;
+				SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
+				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
+				SWIFT_VERSION = 5.0;
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		A100000B2F00000000000001 /* Build configuration list for PBXProject "gramora" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				A100000C2F00000000000001 /* Debug */,
+				A100000D2F00000000000001 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		A10000072F00000000000001 /* Build configuration list for PBXNativeTarget "gramora" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				A100000E2F00000000000001 /* Debug */,
+				A100000F2F00000000000001 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = A100000A2F00000000000001 /* Project object */;
+}

+ 7 - 0
gramora.xcodeproj/project.xcworkspace/contents.xcworkspacedata

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:">
+   </FileRef>
+</Workspace>

+ 12 - 0
gramora.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist

@@ -0,0 +1,12 @@
+<?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>BuildIndependentTargetsInParallel</key>
+	<true/>
+	<key>LastSwiftUpdateCheck</key>
+	<integer>2640</integer>
+	<key>LastUpgradeCheck</key>
+	<integer>2640</integer>
+</dict>
+</plist>

+ 14 - 0
gramora/App/GramoraApp.swift

@@ -0,0 +1,14 @@
+import SwiftUI
+
+@main
+struct GramoraApp: App {
+    var body: some Scene {
+        WindowGroup {
+            MainView()
+                .frame(minWidth: AppTheme.windowMinWidth, minHeight: AppTheme.windowMinHeight)
+        }
+        .defaultSize(width: AppTheme.windowWidth, height: AppTheme.windowHeight)
+        .windowStyle(.hiddenTitleBar)
+        .windowToolbarStyle(.unifiedCompact(showsTitle: false))
+    }
+}

+ 26 - 0
gramora/Info.plist

@@ -0,0 +1,26 @@
+<?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>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>$(MARKETING_VERSION)</string>
+	<key>CFBundleVersion</key>
+	<string>$(CURRENT_PROJECT_VERSION)</string>
+	<key>LSMinimumSystemVersion</key>
+	<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
+	<key>NSHumanReadableCopyright</key>
+	<string></string>
+</dict>
+</plist>

+ 49 - 0
gramora/Models/NavigationDestination.swift

@@ -0,0 +1,49 @@
+import Foundation
+
+enum NavigationDestination: String, CaseIterable, Identifiable {
+    case grammarChecker
+    case summarizer
+    case paraphrasing
+    case languageTranslator
+    case punctuationChecker
+    case wordsCount
+    case aiEmailWriter
+    case spellingChecker
+    case dictionary
+    case journalFinder
+    case history
+
+    var id: String { rawValue }
+
+    var title: String {
+        switch self {
+        case .grammarChecker: "Grammar Checker"
+        case .summarizer: "Summarizer"
+        case .paraphrasing: "Paraphrasing"
+        case .languageTranslator: "Language Translator"
+        case .punctuationChecker: "Punctuation Checker"
+        case .wordsCount: "Words Count"
+        case .aiEmailWriter: "AI Email Writer"
+        case .spellingChecker: "Spelling Checker"
+        case .dictionary: "Dictionary"
+        case .journalFinder: "Journal Finder"
+        case .history: "History"
+        }
+    }
+
+    var iconName: String {
+        switch self {
+        case .grammarChecker: "text.badge.checkmark"
+        case .summarizer: "doc.text"
+        case .paraphrasing: "arrow.triangle.2.circlepath"
+        case .languageTranslator: "globe"
+        case .punctuationChecker: "text.quote"
+        case .wordsCount: "textformat.123"
+        case .aiEmailWriter: "envelope"
+        case .spellingChecker: "checklist"
+        case .dictionary: "book"
+        case .journalFinder: "magnifyingglass"
+        case .history: "clock"
+        }
+    }
+}

+ 20 - 0
gramora/Resources/Assets.xcassets/AccentColor.colorset/Contents.json

@@ -0,0 +1,20 @@
+{
+  "colors" : [
+    {
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "0.490",
+          "green" : "0.710",
+          "red" : "0.180"
+        }
+      },
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 58 - 0
gramora/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json

@@ -0,0 +1,58 @@
+{
+  "images" : [
+    {
+      "idiom" : "mac",
+      "scale" : "1x",
+      "size" : "16x16"
+    },
+    {
+      "idiom" : "mac",
+      "scale" : "2x",
+      "size" : "16x16"
+    },
+    {
+      "idiom" : "mac",
+      "scale" : "1x",
+      "size" : "32x32"
+    },
+    {
+      "idiom" : "mac",
+      "scale" : "2x",
+      "size" : "32x32"
+    },
+    {
+      "idiom" : "mac",
+      "scale" : "1x",
+      "size" : "128x128"
+    },
+    {
+      "idiom" : "mac",
+      "scale" : "2x",
+      "size" : "128x128"
+    },
+    {
+      "idiom" : "mac",
+      "scale" : "1x",
+      "size" : "256x256"
+    },
+    {
+      "idiom" : "mac",
+      "scale" : "2x",
+      "size" : "256x256"
+    },
+    {
+      "idiom" : "mac",
+      "scale" : "1x",
+      "size" : "512x512"
+    },
+    {
+      "idiom" : "mac",
+      "scale" : "2x",
+      "size" : "512x512"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 6 - 0
gramora/Resources/Assets.xcassets/Contents.json

@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 44 - 0
gramora/Utilities/AppTheme.swift

@@ -0,0 +1,44 @@
+import SwiftUI
+
+enum AppTheme {
+    static let windowWidth: CGFloat = 1100
+    static let windowHeight: CGFloat = 720
+    static let windowMinWidth: CGFloat = 900
+    static let windowMinHeight: CGFloat = 600
+
+    static let sidebarWidth: CGFloat = 230
+    static let contentPadding: CGFloat = 32
+    static let cardCornerRadius: CGFloat = 16
+    static let buttonCornerRadius: CGFloat = 10
+    static let navItemCornerRadius: CGFloat = 10
+
+    static let background = Color(red: 0.96, green: 0.97, blue: 0.98)
+    static let sidebarBackground = Color.white
+    static let cardBackground = Color.white
+    static let textPrimary = Color(red: 0.10, green: 0.14, blue: 0.22)
+    static let textSecondary = Color(red: 0.45, green: 0.50, blue: 0.56)
+    static let textMuted = Color(red: 0.62, green: 0.66, blue: 0.70)
+    static let border = Color(red: 0.90, green: 0.92, blue: 0.94)
+    static let inputBorder = Color(red: 0.88, green: 0.90, blue: 0.93)
+
+    static let teal = Color(red: 0.18, green: 0.71, blue: 0.49)
+    static let tealLight = Color(red: 0.91, green: 0.97, blue: 0.94)
+    static let tealDark = Color(red: 0.13, green: 0.60, blue: 0.42)
+    static let gradientStart = Color(red: 0.18, green: 0.71, blue: 0.49)
+    static let gradientEnd = Color(red: 0.34, green: 0.85, blue: 0.63)
+
+    static let premiumBackground = Color(red: 0.91, green: 0.97, blue: 0.95)
+    static let premiumBorder = Color(red: 0.82, green: 0.93, blue: 0.88)
+    static let blobColor = Color(red: 0.18, green: 0.71, blue: 0.49).opacity(0.06)
+    static let dotColor = Color(red: 0.18, green: 0.71, blue: 0.49).opacity(0.12)
+
+    static let cardShadow = Color.black.opacity(0.04)
+
+    static var primaryGradient: LinearGradient {
+        LinearGradient(
+            colors: [gradientStart, gradientEnd],
+            startPoint: .leading,
+            endPoint: .trailing
+        )
+    }
+}

+ 41 - 0
gramora/ViewModels/GrammarCheckerViewModel.swift

@@ -0,0 +1,41 @@
+import AppKit
+import Combine
+import Foundation
+
+@MainActor
+final class GrammarCheckerViewModel: ObservableObject {
+    static let maxCharacters = 10_000
+
+    @Published var text: String = ""
+
+    var characterCount: Int { text.count }
+
+    var wordCount: Int {
+        let components = text
+            .trimmingCharacters(in: .whitespacesAndNewlines)
+            .components(separatedBy: .whitespacesAndNewlines)
+            .filter { !$0.isEmpty }
+        return components.count
+    }
+
+    var characterCountLabel: String {
+        "\(characterCount)/\(Self.maxCharacters)"
+    }
+
+    var wordCountLabel: String {
+        "\(wordCount)/\(Self.maxCharacters) Words"
+    }
+
+    func pasteText() {
+        guard let clipboard = NSPasteboard.general.string(forType: .string) else { return }
+        text = String(clipboard.prefix(Self.maxCharacters))
+    }
+
+    func clearText() {
+        text = ""
+    }
+
+    func checkGrammar() {
+        // Grammar check service integration point
+    }
+}

+ 7 - 0
gramora/ViewModels/MainViewModel.swift

@@ -0,0 +1,7 @@
+import Combine
+import Foundation
+
+@MainActor
+final class MainViewModel: ObservableObject {
+    @Published var selectedDestination: NavigationDestination = .grammarChecker
+}

+ 56 - 0
gramora/Views/Components/BackgroundDecorations.swift

@@ -0,0 +1,56 @@
+import SwiftUI
+
+struct BackgroundDecorations: View {
+    var body: some View {
+        ZStack {
+            AppTheme.background
+
+            Circle()
+                .fill(AppTheme.blobColor)
+                .frame(width: 320, height: 320)
+                .blur(radius: 40)
+                .offset(x: 380, y: -220)
+
+            Circle()
+                .fill(AppTheme.blobColor)
+                .frame(width: 260, height: 260)
+                .blur(radius: 50)
+                .offset(x: -420, y: 280)
+
+            Ellipse()
+                .fill(AppTheme.blobColor.opacity(0.8))
+                .frame(width: 200, height: 140)
+                .blur(radius: 30)
+                .offset(x: 300, y: 200)
+
+            DotGrid()
+                .offset(x: -380, y: 260)
+                .opacity(0.6)
+
+            DotGrid()
+                .offset(x: 420, y: -240)
+                .opacity(0.5)
+        }
+        .ignoresSafeArea()
+    }
+}
+
+private struct DotGrid: View {
+    private let columns = 5
+    private let rows = 5
+    private let spacing: CGFloat = 10
+
+    var body: some View {
+        VStack(spacing: spacing) {
+            ForEach(0..<rows, id: \.self) { _ in
+                HStack(spacing: spacing) {
+                    ForEach(0..<columns, id: \.self) { _ in
+                        Circle()
+                            .fill(AppTheme.dotColor)
+                            .frame(width: 4, height: 4)
+                    }
+                }
+            }
+        }
+    }
+}

+ 80 - 0
gramora/Views/Components/GradientButton.swift

@@ -0,0 +1,80 @@
+import SwiftUI
+
+struct GradientButton: View {
+    let title: String
+    let action: () -> Void
+
+    var body: some View {
+        Button(action: action) {
+            HStack(spacing: 10) {
+                Image(systemName: "sparkles")
+                    .font(.system(size: 15, weight: .semibold))
+
+                Text(title)
+                    .font(.system(size: 16, weight: .semibold))
+
+                Spacer(minLength: 0)
+
+                Image(systemName: "arrow.right")
+                    .font(.system(size: 14, weight: .semibold))
+            }
+            .foregroundStyle(.white)
+            .padding(.horizontal, 24)
+            .padding(.vertical, 14)
+            .frame(maxWidth: .infinity)
+            .background(AppTheme.primaryGradient)
+            .clipShape(RoundedRectangle(cornerRadius: AppTheme.buttonCornerRadius))
+            .shadow(color: AppTheme.teal.opacity(0.25), radius: 8, y: 4)
+        }
+        .buttonStyle(.plain)
+    }
+}
+
+struct ToolbarButton: View {
+    let title: String
+    let iconName: String
+    let action: () -> Void
+
+    var body: some View {
+        Button(action: action) {
+            HStack(spacing: 6) {
+                Image(systemName: iconName)
+                    .font(.system(size: 12))
+
+                Text(title)
+                    .font(.system(size: 12, weight: .medium))
+            }
+            .foregroundStyle(AppTheme.textSecondary)
+            .padding(.horizontal, 12)
+            .padding(.vertical, 7)
+            .background(Color.white)
+            .clipShape(RoundedRectangle(cornerRadius: 8))
+            .overlay(
+                RoundedRectangle(cornerRadius: 8)
+                    .stroke(AppTheme.border, lineWidth: 1)
+            )
+        }
+        .buttonStyle(.plain)
+    }
+}
+
+struct IconButton: View {
+    let iconName: String
+    let action: () -> Void
+
+    var body: some View {
+        Button(action: action) {
+            Image(systemName: iconName)
+                .font(.system(size: 13))
+                .foregroundStyle(AppTheme.textSecondary)
+                .frame(width: 34, height: 34)
+                .background(Color.white)
+                .clipShape(RoundedRectangle(cornerRadius: 8))
+                .overlay(
+                    RoundedRectangle(cornerRadius: 8)
+                        .stroke(AppTheme.border, lineWidth: 1)
+                )
+        }
+        .buttonStyle(.plain)
+    }
+}

+ 50 - 0
gramora/Views/Components/PremiumCardView.swift

@@ -0,0 +1,50 @@
+import SwiftUI
+
+struct PremiumCardView: View {
+    var body: some View {
+        VStack(alignment: .leading, spacing: 10) {
+            HStack(spacing: 8) {
+                Image(systemName: "crown.fill")
+                    .font(.system(size: 14))
+                    .foregroundStyle(Color(red: 0.95, green: 0.75, blue: 0.20))
+
+                Text("Unlock Premium")
+                    .font(.system(size: 13, weight: .semibold))
+                    .foregroundStyle(AppTheme.textPrimary)
+            }
+
+            Text("Get unlimited checks, advanced features and an ad-free experience.")
+                .font(.system(size: 11))
+                .foregroundStyle(AppTheme.textSecondary)
+                .lineSpacing(2)
+                .fixedSize(horizontal: false, vertical: true)
+
+            Button(action: {}) {
+                HStack(spacing: 6) {
+                    Text("Upgrade Now")
+                        .font(.system(size: 12, weight: .semibold))
+
+                    Image(systemName: "arrow.up.right")
+                        .font(.system(size: 10, weight: .semibold))
+                }
+                .foregroundStyle(AppTheme.teal)
+                .frame(maxWidth: .infinity)
+                .padding(.vertical, 8)
+                .background(Color.white)
+                .clipShape(RoundedRectangle(cornerRadius: 8))
+                .overlay(
+                    RoundedRectangle(cornerRadius: 8)
+                        .stroke(AppTheme.premiumBorder, lineWidth: 1)
+                )
+            }
+            .buttonStyle(.plain)
+        }
+        .padding(14)
+        .background(AppTheme.premiumBackground)
+        .clipShape(RoundedRectangle(cornerRadius: 12))
+        .overlay(
+            RoundedRectangle(cornerRadius: 12)
+                .stroke(AppTheme.premiumBorder, lineWidth: 1)
+        )
+    }
+}

+ 138 - 0
gramora/Views/GrammarCheckerView.swift

@@ -0,0 +1,138 @@
+import SwiftUI
+
+struct GrammarCheckerView: View {
+    @StateObject private var viewModel = GrammarCheckerViewModel()
+
+    var body: some View {
+        VStack(alignment: .leading, spacing: 0) {
+            header
+                .padding(.bottom, 24)
+
+            contentCard
+                .padding(.bottom, 20)
+
+            GradientButton(title: "Check Grammar") {
+                viewModel.checkGrammar()
+            }
+        }
+        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
+        .padding(.horizontal, AppTheme.contentPadding)
+        .padding(.top, 28)
+        .padding(.bottom, 32)
+        .onChange(of: viewModel.text) { _, newValue in
+            if newValue.count > GrammarCheckerViewModel.maxCharacters {
+                viewModel.text = String(newValue.prefix(GrammarCheckerViewModel.maxCharacters))
+            }
+        }
+    }
+
+    private var header: some View {
+        HStack(alignment: .top) {
+            VStack(alignment: .leading, spacing: 6) {
+                Text("Grammar Checker")
+                    .font(.system(size: 28, weight: .bold))
+                    .foregroundStyle(AppTheme.textPrimary)
+
+                HStack(spacing: 0) {
+                    Text("Write ")
+                        .foregroundStyle(AppTheme.textSecondary)
+                    Text("better")
+                        .foregroundStyle(AppTheme.teal)
+                        .fontWeight(.semibold)
+                    Text(". Sound ")
+                        .foregroundStyle(AppTheme.textSecondary)
+                    Text("smarter")
+                        .foregroundStyle(AppTheme.teal)
+                        .fontWeight(.semibold)
+                    Text(".")
+                        .foregroundStyle(AppTheme.textSecondary)
+                }
+                .font(.system(size: 14))
+            }
+
+            Spacer()
+
+            IconButton(iconName: "gearshape") {}
+        }
+    }
+
+    private var contentCard: some View {
+        VStack(alignment: .leading, spacing: 14) {
+            HStack(spacing: 8) {
+                Image(systemName: "pencil.line")
+                    .font(.system(size: 13))
+                    .foregroundStyle(AppTheme.teal)
+
+                Text("Your Content")
+                    .font(.system(size: 14, weight: .semibold))
+                    .foregroundStyle(AppTheme.textPrimary)
+            }
+
+            ZStack(alignment: .bottomTrailing) {
+                TextEditor(text: $viewModel.text)
+                    .font(.system(size: 14))
+                    .foregroundStyle(AppTheme.textPrimary)
+                    .scrollContentBackground(.hidden)
+                    .padding(12)
+                    .frame(minHeight: 220)
+                    .background(Color.white)
+                    .clipShape(RoundedRectangle(cornerRadius: 10))
+                    .overlay(
+                        RoundedRectangle(cornerRadius: 10)
+                            .stroke(AppTheme.inputBorder, lineWidth: 1)
+                    )
+                    .overlay(alignment: .topLeading) {
+                        if viewModel.text.isEmpty {
+                            Text("Type or paste your text here and press the check button...")
+                                .font(.system(size: 14))
+                                .foregroundStyle(AppTheme.textMuted)
+                                .padding(.horizontal, 16)
+                                .padding(.vertical, 20)
+                                .allowsHitTesting(false)
+                        }
+                    }
+
+                Text(viewModel.characterCountLabel)
+                    .font(.system(size: 11))
+                    .foregroundStyle(AppTheme.textMuted)
+                    .padding(.trailing, 14)
+                    .padding(.bottom, 10)
+            }
+
+            HStack(spacing: 8) {
+                ToolbarButton(title: "Paste Text", iconName: "doc.on.clipboard") {
+                    viewModel.pasteText()
+                }
+
+                ToolbarButton(title: "Upload File", iconName: "arrow.up.doc") {}
+
+                HStack(spacing: 6) {
+                    Image(systemName: "textformat")
+                        .font(.system(size: 12))
+
+                    Text(viewModel.wordCountLabel)
+                        .font(.system(size: 12, weight: .medium))
+                }
+                .foregroundStyle(AppTheme.textSecondary)
+                .padding(.horizontal, 12)
+                .padding(.vertical, 7)
+                .background(Color.white)
+                .clipShape(RoundedRectangle(cornerRadius: 8))
+                .overlay(
+                    RoundedRectangle(cornerRadius: 8)
+                        .stroke(AppTheme.border, lineWidth: 1)
+                )
+
+                Spacer()
+
+                IconButton(iconName: "eraser") {
+                    viewModel.clearText()
+                }
+            }
+        }
+        .padding(20)
+        .background(AppTheme.cardBackground)
+        .clipShape(RoundedRectangle(cornerRadius: AppTheme.cardCornerRadius))
+        .shadow(color: AppTheme.cardShadow, radius: 12, y: 4)
+    }
+}

+ 30 - 0
gramora/Views/MainView.swift

@@ -0,0 +1,30 @@
+import SwiftUI
+
+struct MainView: View {
+    @StateObject private var viewModel = MainViewModel()
+
+    var body: some View {
+        HStack(spacing: 0) {
+            SidebarView(selectedDestination: $viewModel.selectedDestination)
+
+            ZStack {
+                BackgroundDecorations()
+
+                contentView
+                    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
+            }
+            .frame(maxWidth: .infinity, maxHeight: .infinity)
+        }
+        .background(AppTheme.background)
+    }
+
+    @ViewBuilder
+    private var contentView: some View {
+        switch viewModel.selectedDestination {
+        case .grammarChecker:
+            GrammarCheckerView()
+        default:
+            PlaceholderView(destination: viewModel.selectedDestination)
+        }
+    }
+}

+ 22 - 0
gramora/Views/PlaceholderView.swift

@@ -0,0 +1,22 @@
+import SwiftUI
+
+struct PlaceholderView: View {
+    let destination: NavigationDestination
+
+    var body: some View {
+        VStack(spacing: 12) {
+            Image(systemName: destination.iconName)
+                .font(.system(size: 40))
+                .foregroundStyle(AppTheme.teal.opacity(0.5))
+
+            Text(destination.title)
+                .font(.system(size: 24, weight: .bold))
+                .foregroundStyle(AppTheme.textPrimary)
+
+            Text("Coming soon")
+                .font(.system(size: 14))
+                .foregroundStyle(AppTheme.textSecondary)
+        }
+        .frame(maxWidth: .infinity, maxHeight: .infinity)
+    }
+}

+ 97 - 0
gramora/Views/SidebarView.swift

@@ -0,0 +1,97 @@
+import SwiftUI
+
+struct SidebarView: View {
+    @Binding var selectedDestination: NavigationDestination
+
+    var body: some View {
+        VStack(alignment: .leading, spacing: 0) {
+            logoHeader
+                .padding(.horizontal, 20)
+                .padding(.top, 28)
+                .padding(.bottom, 24)
+
+            ScrollView(.vertical, showsIndicators: false) {
+                VStack(spacing: 2) {
+                    ForEach(NavigationDestination.allCases) { destination in
+                        SidebarNavItem(
+                            destination: destination,
+                            isSelected: selectedDestination == destination
+                        ) {
+                            selectedDestination = destination
+                        }
+                    }
+                }
+                .padding(.horizontal, 12)
+            }
+
+            Spacer(minLength: 16)
+
+            PremiumCardView()
+                .padding(.horizontal, 16)
+                .padding(.bottom, 20)
+        }
+        .frame(width: AppTheme.sidebarWidth)
+        .background(AppTheme.sidebarBackground)
+        .overlay(alignment: .trailing) {
+            Rectangle()
+                .fill(AppTheme.border)
+                .frame(width: 1)
+        }
+    }
+
+    private var logoHeader: some View {
+        HStack(spacing: 10) {
+            ZStack {
+                Circle()
+                    .fill(AppTheme.teal)
+                    .frame(width: 36, height: 36)
+
+                Text("G")
+                    .font(.system(size: 18, weight: .bold))
+                    .foregroundStyle(.white)
+            }
+
+            Text("Grammo")
+                .font(.system(size: 18, weight: .bold))
+                .foregroundStyle(AppTheme.textPrimary)
+        }
+    }
+}
+
+private struct SidebarNavItem: View {
+    let destination: NavigationDestination
+    let isSelected: Bool
+    let action: () -> Void
+
+    var body: some View {
+        Button(action: action) {
+            HStack(spacing: 10) {
+                Image(systemName: destination.iconName)
+                    .font(.system(size: 13))
+                    .frame(width: 18)
+
+                Text(destination.title)
+                    .font(.system(size: 13, weight: isSelected ? .semibold : .regular))
+                    .lineLimit(1)
+
+                Spacer()
+            }
+            .foregroundStyle(isSelected ? AppTheme.teal : AppTheme.textSecondary)
+            .padding(.horizontal, 12)
+            .padding(.vertical, 9)
+            .background(
+                RoundedRectangle(cornerRadius: AppTheme.navItemCornerRadius)
+                    .fill(isSelected ? AppTheme.tealLight : Color.clear)
+            )
+            .overlay(alignment: .leading) {
+                if isSelected {
+                    RoundedRectangle(cornerRadius: 2)
+                        .fill(AppTheme.teal)
+                        .frame(width: 3, height: 20)
+                        .offset(x: -4)
+                }
+            }
+        }
+        .buttonStyle(.plain)
+    }
+}

+ 10 - 0
gramora/gramora.entitlements

@@ -0,0 +1,10 @@
+<?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.security.app-sandbox</key>
+	<true/>
+	<key>com.apple.security.files.user-selected.read-only</key>
+	<true/>
+</dict>
+</plist>