Procházet zdrojové kódy

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 před 1 měsícem
revize
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>