diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c15ea64
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+.vscode
+build
+Accento.zip
+diacritics-macos.xcodeproj/project.xcworkspace/xcuserdata/
+diacritics-macos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c578986
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Matyáš Kříž
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..81bff76
--- /dev/null
+++ b/README.md
@@ -0,0 +1,22 @@
+
+
+
+
+# Acčento
+##### [Ak·čento]
+
+---
+
+Acčento is an easy-to-use tool for adding Czech diacritics to copied text. The app lives in the menu bar and is activated using a global hot key.
+
+The app also contains preferences allowing you to customize the hot key as well as other quality of life features.
+
+Props to [](https://www.nechybujte.cz/nastroje) for providing the incredible diacritics service.
+
+## Installation
+Visit [Releases](https://github.com/MatyasKriz/Accento/releases) and download a `.zip` file of the latest version, open it, move the app into the `Applications` folder and launch it.
+
+### Contributing
+If you'd like to see a feature implemented, feel free to open a pull request or just a proposal as an issue.
+
+This project isn't following any best practices; that said, please try to keep the code clean and simple. Same with commit messages. Thanks!
diff --git a/diacritics-macos.xcodeproj/project.pbxproj b/diacritics-macos.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..4c101db
--- /dev/null
+++ b/diacritics-macos.xcodeproj/project.pbxproj
@@ -0,0 +1,485 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 52;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 68B1C7F926DD3A4400A7E552 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68B1C7F826DD3A4400A7E552 /* AppDelegate.swift */; };
+ 68B1C7FB26DD3A4400A7E552 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68B1C7FA26DD3A4400A7E552 /* ContentView.swift */; };
+ 68B1C7FD26DD3A4600A7E552 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 68B1C7FC26DD3A4600A7E552 /* Assets.xcassets */; };
+ 68B1C80026DD3A4600A7E552 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 68B1C7FF26DD3A4600A7E552 /* Preview Assets.xcassets */; };
+ 68B1C80326DD3A4600A7E552 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 68B1C80126DD3A4600A7E552 /* Main.storyboard */; };
+ 68B1C80E26DDF00500A7E552 /* GlobalHotKeyService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68B1C80D26DDF00500A7E552 /* GlobalHotKeyService.swift */; };
+ 68B1C81026DDF00F00A7E552 /* DiacriticsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68B1C80F26DDF00F00A7E552 /* DiacriticsService.swift */; };
+ 68B1C81226DDF07500A7E552 /* String+NSRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68B1C81126DDF07500A7E552 /* String+NSRange.swift */; };
+ 68B1C81926DE06C200A7E552 /* StorageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68B1C81826DE06C200A7E552 /* StorageService.swift */; };
+ 68B1C81B26DE06D600A7E552 /* HotKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68B1C81A26DE06D600A7E552 /* HotKey.swift */; };
+ 68C063D226E0A8BD00151BCA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 68C063D426E0A8BD00151BCA /* Localizable.strings */; };
+ 68C063D626E0A9F100151BCA /* RoundedCorners.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68C063D526E0A9F100151BCA /* RoundedCorners.swift */; };
+ 68C063D826E0AEC800151BCA /* Shape+extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68C063D726E0AEC800151BCA /* Shape+extensions.swift */; };
+ 68C063DA26E1291B00151BCA /* Color+assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68C063D926E1291B00151BCA /* Color+assets.swift */; };
+ 68C063DC26E129D500151BCA /* String+localized.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68C063DB26E129D500151BCA /* String+localized.swift */; };
+ 68C063DF26E1F4A500151BCA /* HotKeyBinderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68C063DE26E1F4A500151BCA /* HotKeyBinderView.swift */; };
+ 68C063E226E73F6400151BCA /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = 68C063E126E73F6400151BCA /* LaunchAtLogin */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 68B1C7F526DD3A4300A7E552 /* diacritics-macos.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "diacritics-macos.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 68B1C7F826DD3A4400A7E552 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 68B1C7FA26DD3A4400A7E552 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
+ 68B1C7FC26DD3A4600A7E552 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 68B1C7FF26DD3A4600A7E552 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
+ 68B1C80226DD3A4600A7E552 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 68B1C80426DD3A4600A7E552 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 68B1C80526DD3A4600A7E552 /* diacritics_macos.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = diacritics_macos.entitlements; sourceTree = ""; };
+ 68B1C80D26DDF00500A7E552 /* GlobalHotKeyService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalHotKeyService.swift; sourceTree = ""; };
+ 68B1C80F26DDF00F00A7E552 /* DiacriticsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiacriticsService.swift; sourceTree = ""; };
+ 68B1C81126DDF07500A7E552 /* String+NSRange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+NSRange.swift"; sourceTree = ""; };
+ 68B1C81826DE06C200A7E552 /* StorageService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageService.swift; sourceTree = ""; };
+ 68B1C81A26DE06D600A7E552 /* HotKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotKey.swift; sourceTree = ""; };
+ 68C063D326E0A8BD00151BCA /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; };
+ 68C063D526E0A9F100151BCA /* RoundedCorners.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedCorners.swift; sourceTree = ""; };
+ 68C063D726E0AEC800151BCA /* Shape+extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Shape+extensions.swift"; sourceTree = ""; };
+ 68C063D926E1291B00151BCA /* Color+assets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+assets.swift"; sourceTree = ""; };
+ 68C063DB26E129D500151BCA /* String+localized.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+localized.swift"; sourceTree = ""; };
+ 68C063DD26E12D4800151BCA /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = ""; };
+ 68C063DE26E1F4A500151BCA /* HotKeyBinderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotKeyBinderView.swift; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 68B1C7F226DD3A4300A7E552 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 68C063E226E73F6400151BCA /* LaunchAtLogin in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 68B1C7EC26DD3A4300A7E552 = {
+ isa = PBXGroup;
+ children = (
+ 68B1C7F726DD3A4300A7E552 /* diacritics-macos */,
+ 68B1C7F626DD3A4300A7E552 /* Products */,
+ );
+ sourceTree = "";
+ };
+ 68B1C7F626DD3A4300A7E552 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 68B1C7F526DD3A4300A7E552 /* diacritics-macos.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 68B1C7F726DD3A4300A7E552 /* diacritics-macos */ = {
+ isa = PBXGroup;
+ children = (
+ 68B1C7F826DD3A4400A7E552 /* AppDelegate.swift */,
+ 68B1C7FA26DD3A4400A7E552 /* ContentView.swift */,
+ 68C063DE26E1F4A500151BCA /* HotKeyBinderView.swift */,
+ 68B1C80C26DDEFFA00A7E552 /* Services */,
+ 68C063CE26E0A8A900151BCA /* Resources */,
+ 68B1C80B26DDEFF800A7E552 /* Utils */,
+ 68B1C7FC26DD3A4600A7E552 /* Assets.xcassets */,
+ 68B1C80126DD3A4600A7E552 /* Main.storyboard */,
+ 68B1C80426DD3A4600A7E552 /* Info.plist */,
+ 68B1C80526DD3A4600A7E552 /* diacritics_macos.entitlements */,
+ 68B1C7FE26DD3A4600A7E552 /* Preview Content */,
+ );
+ path = "diacritics-macos";
+ sourceTree = "";
+ };
+ 68B1C7FE26DD3A4600A7E552 /* Preview Content */ = {
+ isa = PBXGroup;
+ children = (
+ 68B1C7FF26DD3A4600A7E552 /* Preview Assets.xcassets */,
+ );
+ path = "Preview Content";
+ sourceTree = "";
+ };
+ 68B1C80B26DDEFF800A7E552 /* Utils */ = {
+ isa = PBXGroup;
+ children = (
+ 68B1C81126DDF07500A7E552 /* String+NSRange.swift */,
+ 68B1C81A26DE06D600A7E552 /* HotKey.swift */,
+ 68C063D526E0A9F100151BCA /* RoundedCorners.swift */,
+ 68C063D726E0AEC800151BCA /* Shape+extensions.swift */,
+ 68C063D926E1291B00151BCA /* Color+assets.swift */,
+ 68C063DB26E129D500151BCA /* String+localized.swift */,
+ );
+ path = Utils;
+ sourceTree = "";
+ };
+ 68B1C80C26DDEFFA00A7E552 /* Services */ = {
+ isa = PBXGroup;
+ children = (
+ 68B1C80D26DDF00500A7E552 /* GlobalHotKeyService.swift */,
+ 68B1C80F26DDF00F00A7E552 /* DiacriticsService.swift */,
+ 68B1C81826DE06C200A7E552 /* StorageService.swift */,
+ );
+ path = Services;
+ sourceTree = "";
+ };
+ 68C063CE26E0A8A900151BCA /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 68C063D426E0A8BD00151BCA /* Localizable.strings */,
+ );
+ path = Resources;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 68B1C7F426DD3A4300A7E552 /* diacritics-macos */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 68B1C80826DD3A4600A7E552 /* Build configuration list for PBXNativeTarget "diacritics-macos" */;
+ buildPhases = (
+ 68B1C7F126DD3A4300A7E552 /* Sources */,
+ 68B1C7F226DD3A4300A7E552 /* Frameworks */,
+ 68B1C7F326DD3A4300A7E552 /* Resources */,
+ 68AD2E9726E8C2C700EF31FC /* Copy "Launch at Login Helper" */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "diacritics-macos";
+ packageProductDependencies = (
+ 68C063E126E73F6400151BCA /* LaunchAtLogin */,
+ );
+ productName = "diacritics-macos";
+ productReference = 68B1C7F526DD3A4300A7E552 /* diacritics-macos.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 68B1C7ED26DD3A4300A7E552 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 1250;
+ LastUpgradeCheck = 1250;
+ TargetAttributes = {
+ 68B1C7F426DD3A4300A7E552 = {
+ CreatedOnToolsVersion = 12.5.1;
+ };
+ };
+ };
+ buildConfigurationList = 68B1C7F026DD3A4300A7E552 /* Build configuration list for PBXProject "diacritics-macos" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ cs,
+ );
+ mainGroup = 68B1C7EC26DD3A4300A7E552;
+ packageReferences = (
+ 68C063E026E73F6400151BCA /* XCRemoteSwiftPackageReference "LaunchAtLogin" */,
+ );
+ productRefGroup = 68B1C7F626DD3A4300A7E552 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 68B1C7F426DD3A4300A7E552 /* diacritics-macos */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 68B1C7F326DD3A4300A7E552 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 68B1C80326DD3A4600A7E552 /* Main.storyboard in Resources */,
+ 68C063D226E0A8BD00151BCA /* Localizable.strings in Resources */,
+ 68B1C80026DD3A4600A7E552 /* Preview Assets.xcassets in Resources */,
+ 68B1C7FD26DD3A4600A7E552 /* Assets.xcassets in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 68AD2E9726E8C2C700EF31FC /* Copy "Launch at Login Helper" */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ name = "Copy \"Launch at Login Helper\"";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${BUILT_PRODUCTS_DIR}/LaunchAtLogin_LaunchAtLogin.bundle/Contents/Resources/copy-helper-swiftpm.sh\"\n";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 68B1C7F126DD3A4300A7E552 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 68B1C81226DDF07500A7E552 /* String+NSRange.swift in Sources */,
+ 68B1C7FB26DD3A4400A7E552 /* ContentView.swift in Sources */,
+ 68C063DC26E129D500151BCA /* String+localized.swift in Sources */,
+ 68C063DA26E1291B00151BCA /* Color+assets.swift in Sources */,
+ 68C063DF26E1F4A500151BCA /* HotKeyBinderView.swift in Sources */,
+ 68B1C81B26DE06D600A7E552 /* HotKey.swift in Sources */,
+ 68C063D826E0AEC800151BCA /* Shape+extensions.swift in Sources */,
+ 68B1C80E26DDF00500A7E552 /* GlobalHotKeyService.swift in Sources */,
+ 68C063D626E0A9F100151BCA /* RoundedCorners.swift in Sources */,
+ 68B1C81026DDF00F00A7E552 /* DiacriticsService.swift in Sources */,
+ 68B1C7F926DD3A4400A7E552 /* AppDelegate.swift in Sources */,
+ 68B1C81926DE06C200A7E552 /* StorageService.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 68B1C80126DD3A4600A7E552 /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 68B1C80226DD3A4600A7E552 /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "";
+ };
+ 68C063D426E0A8BD00151BCA /* Localizable.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 68C063D326E0A8BD00151BCA /* en */,
+ 68C063DD26E12D4800151BCA /* cs */,
+ );
+ name = Localizable.strings;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 68B1C80626DD3A4600A7E552 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ 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;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ 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;
+ MACOSX_DEPLOYMENT_TARGET = 11.3;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 68B1C80726DD3A4600A7E552 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ 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;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ 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;
+ MACOSX_DEPLOYMENT_TARGET = 11.3;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Release;
+ };
+ 68B1C80926DD3A4600A7E552 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_ENTITLEMENTS = "diacritics-macos/diacritics_macos.entitlements";
+ CODE_SIGN_IDENTITY = "-";
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ DEVELOPMENT_ASSET_PATHS = "\"diacritics-macos/Preview Content\"";
+ DEVELOPMENT_TEAM = Y3NT3WUF44;
+ ENABLE_HARDENED_RUNTIME = YES;
+ ENABLE_PREVIEWS = YES;
+ INFOPLIST_FILE = "diacritics-macos/Info.plist";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 11.0;
+ PRODUCT_BUNDLE_IDENTIFIER = "cz.tyas.diacritics-macos";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ 68B1C80A26DD3A4600A7E552 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_ENTITLEMENTS = "diacritics-macos/diacritics_macos.entitlements";
+ CODE_SIGN_IDENTITY = "-";
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ DEVELOPMENT_ASSET_PATHS = "\"diacritics-macos/Preview Content\"";
+ DEVELOPMENT_TEAM = Y3NT3WUF44;
+ ENABLE_HARDENED_RUNTIME = YES;
+ ENABLE_PREVIEWS = YES;
+ INFOPLIST_FILE = "diacritics-macos/Info.plist";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 11.0;
+ PRODUCT_BUNDLE_IDENTIFIER = "cz.tyas.diacritics-macos";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 68B1C7F026DD3A4300A7E552 /* Build configuration list for PBXProject "diacritics-macos" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 68B1C80626DD3A4600A7E552 /* Debug */,
+ 68B1C80726DD3A4600A7E552 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 68B1C80826DD3A4600A7E552 /* Build configuration list for PBXNativeTarget "diacritics-macos" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 68B1C80926DD3A4600A7E552 /* Debug */,
+ 68B1C80A26DD3A4600A7E552 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+
+/* Begin XCRemoteSwiftPackageReference section */
+ 68C063E026E73F6400151BCA /* XCRemoteSwiftPackageReference "LaunchAtLogin" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/sindresorhus/LaunchAtLogin";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 4.1.0;
+ };
+ };
+/* End XCRemoteSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+ 68C063E126E73F6400151BCA /* LaunchAtLogin */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 68C063E026E73F6400151BCA /* XCRemoteSwiftPackageReference "LaunchAtLogin" */;
+ productName = LaunchAtLogin;
+ };
+/* End XCSwiftPackageProductDependency section */
+ };
+ rootObject = 68B1C7ED26DD3A4300A7E552 /* Project object */;
+}
diff --git a/diacritics-macos.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/diacritics-macos.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
new file mode 100644
index 0000000..1f5ee3f
--- /dev/null
+++ b/diacritics-macos.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -0,0 +1,16 @@
+{
+ "object": {
+ "pins": [
+ {
+ "package": "LaunchAtLogin",
+ "repositoryURL": "https://github.com/sindresorhus/LaunchAtLogin",
+ "state": {
+ "branch": null,
+ "revision": "6b16bcdf7d45a9d76a768a5c4912dde925cf0e95",
+ "version": "4.1.0"
+ }
+ }
+ ]
+ },
+ "version": 1
+}
diff --git a/diacritics-macos.xcodeproj/xcshareddata/xcschemes/diacritics-macos.xcscheme b/diacritics-macos.xcodeproj/xcshareddata/xcschemes/diacritics-macos.xcscheme
new file mode 100644
index 0000000..14cd26f
--- /dev/null
+++ b/diacritics-macos.xcodeproj/xcshareddata/xcschemes/diacritics-macos.xcscheme
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/diacritics-macos.xcodeproj/xcuserdata/matyas.xcuserdatad/xcschemes/xcschememanagement.plist b/diacritics-macos.xcodeproj/xcuserdata/matyas.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..432ce86
--- /dev/null
+++ b/diacritics-macos.xcodeproj/xcuserdata/matyas.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,22 @@
+
+
+
+
+ SchemeUserState
+
+ diacritics-macos.xcscheme_^#shared#^_
+
+ orderHint
+ 0
+
+
+ SuppressBuildableAutocreation
+
+ 68B1C7F426DD3A4300A7E552
+
+ primary
+
+
+
+
+
diff --git a/diacritics-macos/AppDelegate.swift b/diacritics-macos/AppDelegate.swift
new file mode 100644
index 0000000..07a1b96
--- /dev/null
+++ b/diacritics-macos/AppDelegate.swift
@@ -0,0 +1,80 @@
+import Cocoa
+import Carbon
+import SwiftUI
+
+@main
+class AppDelegate: NSObject, NSApplicationDelegate {
+
+ private var window: NSWindow!
+
+ private var popover: NSPopover!
+ private var statusBarItem: NSStatusItem!
+
+ func applicationDidFinishLaunching(_ aNotification: Notification) {
+ GlobalHotKeyService.installHandler()
+
+ let isInitialRegistrationSuccessful: Bool
+ if let initialHotKey = StorageService.hotKey {
+ isInitialRegistrationSuccessful = GlobalHotKeyService.registerHotKey(hotKey: initialHotKey)
+ } else {
+ isInitialRegistrationSuccessful = true
+ }
+
+ // Create the SwiftUI view that provides the window contents.
+ let contentView = ContentView(
+ hotKey: StorageService.hotKey,
+ isErrored: !isInitialRegistrationSuccessful
+ )
+
+ popover = NSPopover()
+ popover.contentSize = NSSize(width: 240, height: 360)
+ popover.behavior = .transient
+ popover.contentViewController = NSHostingController(rootView: contentView)
+
+ statusBarItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
+ constructMenu()
+
+ if let button = statusBarItem.button {
+ button.image = NSImage(named: "logo")
+ button.image?.size = NSSize(width: 18.0, height: 18.0)
+ }
+ }
+
+ private func constructMenu() {
+ let menu = NSMenu()
+
+ menu.addItem(NSMenuItem(title: "Menu.Diacriticize".localized, action: #selector(AppDelegate.addDiacritics(_:)), keyEquivalent: ""))
+ menu.addItem(NSMenuItem(title: "Menu.Preferences".localized, action: #selector(AppDelegate.togglePopover(_:)), keyEquivalent: ","))
+ menu.addItem(NSMenuItem.separator())
+ menu.addItem(NSMenuItem(title: "Menu.Quit".localized, action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))
+
+ statusBarItem.menu = menu
+ }
+
+ func applicationWillTerminate(_ aNotification: Notification) {
+ // Insert code here to tear down your application
+ }
+
+ @objc
+ func togglePopover(_ sender: AnyObject?) {
+ if let button = statusBarItem.button {
+ if popover.isShown {
+ popover.performClose(sender)
+ } else {
+ popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY)
+ popover.contentViewController?.view.window?.becomeKey()
+ }
+ }
+ }
+
+ @objc
+ func addDiacritics(_ sender: AnyObject?) {
+ DiacriticsService.addDiacriticsToClipboardText()
+ }
+}
+
+func hotKeyHandler(nextHandler: EventHandlerCallRef?, eventRef: EventRef?, userData: UnsafeMutableRawPointer?) -> OSStatus {
+ DiacriticsService.addDiacriticsToClipboardText()
+
+ return noErr
+}
diff --git a/diacritics-macos/Assets.xcassets/AccentColor.colorset/Contents.json b/diacritics-macos/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 0000000..eb87897
--- /dev/null
+++ b/diacritics-macos/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-1024.png b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-1024.png
new file mode 100644
index 0000000..1926522
Binary files /dev/null and b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-1024.png differ
diff --git a/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-128.png b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-128.png
new file mode 100644
index 0000000..3d62d31
Binary files /dev/null and b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-128.png differ
diff --git a/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-16.png b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-16.png
new file mode 100644
index 0000000..2920411
Binary files /dev/null and b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-16.png differ
diff --git a/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-256.png b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-256.png
new file mode 100644
index 0000000..c06847d
Binary files /dev/null and b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-256.png differ
diff --git a/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-257.png b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-257.png
new file mode 100644
index 0000000..c06847d
Binary files /dev/null and b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-257.png differ
diff --git a/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-32.png b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-32.png
new file mode 100644
index 0000000..7d7819f
Binary files /dev/null and b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-32.png differ
diff --git a/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-33.png b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-33.png
new file mode 100644
index 0000000..7d7819f
Binary files /dev/null and b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-33.png differ
diff --git a/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-512.png b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-512.png
new file mode 100644
index 0000000..33a26d4
Binary files /dev/null and b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-512.png differ
diff --git a/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-513.png b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-513.png
new file mode 100644
index 0000000..33a26d4
Binary files /dev/null and b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-513.png differ
diff --git a/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-64.png b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-64.png
new file mode 100644
index 0000000..5adc96e
Binary files /dev/null and b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/AppIcon-64.png differ
diff --git a/diacritics-macos/Assets.xcassets/AppIcon.appiconset/Contents.json b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..b9ec67d
--- /dev/null
+++ b/diacritics-macos/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,68 @@
+{
+ "images" : [
+ {
+ "filename" : "AppIcon-16.png",
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "16x16"
+ },
+ {
+ "filename" : "AppIcon-32.png",
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "16x16"
+ },
+ {
+ "filename" : "AppIcon-33.png",
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "32x32"
+ },
+ {
+ "filename" : "AppIcon-64.png",
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "32x32"
+ },
+ {
+ "filename" : "AppIcon-128.png",
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "128x128"
+ },
+ {
+ "filename" : "AppIcon-256.png",
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "128x128"
+ },
+ {
+ "filename" : "AppIcon-257.png",
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "256x256"
+ },
+ {
+ "filename" : "AppIcon-513.png",
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "256x256"
+ },
+ {
+ "filename" : "AppIcon-512.png",
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "512x512"
+ },
+ {
+ "filename" : "AppIcon-1024.png",
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "512x512"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/diacritics-macos/Assets.xcassets/Contents.json b/diacritics-macos/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/diacritics-macos/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/diacritics-macos/Assets.xcassets/blurple.colorset/Contents.json b/diacritics-macos/Assets.xcassets/blurple.colorset/Contents.json
new file mode 100644
index 0000000..15035b0
--- /dev/null
+++ b/diacritics-macos/Assets.xcassets/blurple.colorset/Contents.json
@@ -0,0 +1,29 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xF2",
+ "green" : "0x65",
+ "red" : "0x58"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/diacritics-macos/Assets.xcassets/crimson.colorset/Contents.json b/diacritics-macos/Assets.xcassets/crimson.colorset/Contents.json
new file mode 100644
index 0000000..3e80f83
--- /dev/null
+++ b/diacritics-macos/Assets.xcassets/crimson.colorset/Contents.json
@@ -0,0 +1,29 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x3C",
+ "green" : "0x14",
+ "red" : "0xDC"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/diacritics-macos/Assets.xcassets/lingea.imageset/Contents.json b/diacritics-macos/Assets.xcassets/lingea.imageset/Contents.json
new file mode 100644
index 0000000..7d40d34
--- /dev/null
+++ b/diacritics-macos/Assets.xcassets/lingea.imageset/Contents.json
@@ -0,0 +1,26 @@
+{
+ "images" : [
+ {
+ "filename" : "lingea.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "lingea-1.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "lingea-2.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "template-rendering-intent" : "template"
+ }
+}
diff --git a/diacritics-macos/Assets.xcassets/lingea.imageset/lingea-1.png b/diacritics-macos/Assets.xcassets/lingea.imageset/lingea-1.png
new file mode 100644
index 0000000..0a701a1
Binary files /dev/null and b/diacritics-macos/Assets.xcassets/lingea.imageset/lingea-1.png differ
diff --git a/diacritics-macos/Assets.xcassets/lingea.imageset/lingea-2.png b/diacritics-macos/Assets.xcassets/lingea.imageset/lingea-2.png
new file mode 100644
index 0000000..0a701a1
Binary files /dev/null and b/diacritics-macos/Assets.xcassets/lingea.imageset/lingea-2.png differ
diff --git a/diacritics-macos/Assets.xcassets/lingea.imageset/lingea.png b/diacritics-macos/Assets.xcassets/lingea.imageset/lingea.png
new file mode 100644
index 0000000..0a701a1
Binary files /dev/null and b/diacritics-macos/Assets.xcassets/lingea.imageset/lingea.png differ
diff --git a/diacritics-macos/Assets.xcassets/logo.imageset/Contents.json b/diacritics-macos/Assets.xcassets/logo.imageset/Contents.json
new file mode 100644
index 0000000..02d1de3
--- /dev/null
+++ b/diacritics-macos/Assets.xcassets/logo.imageset/Contents.json
@@ -0,0 +1,26 @@
+{
+ "images" : [
+ {
+ "filename" : "logo.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "logo@2x.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "logo@3x.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "template-rendering-intent" : "template"
+ }
+}
diff --git a/diacritics-macos/Assets.xcassets/logo.imageset/logo.png b/diacritics-macos/Assets.xcassets/logo.imageset/logo.png
new file mode 100644
index 0000000..46f96f0
Binary files /dev/null and b/diacritics-macos/Assets.xcassets/logo.imageset/logo.png differ
diff --git a/diacritics-macos/Assets.xcassets/logo.imageset/logo@2x.png b/diacritics-macos/Assets.xcassets/logo.imageset/logo@2x.png
new file mode 100644
index 0000000..e0e7b26
Binary files /dev/null and b/diacritics-macos/Assets.xcassets/logo.imageset/logo@2x.png differ
diff --git a/diacritics-macos/Assets.xcassets/logo.imageset/logo@3x.png b/diacritics-macos/Assets.xcassets/logo.imageset/logo@3x.png
new file mode 100644
index 0000000..b664283
Binary files /dev/null and b/diacritics-macos/Assets.xcassets/logo.imageset/logo@3x.png differ
diff --git a/diacritics-macos/Base.lproj/Main.storyboard b/diacritics-macos/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..d7ebe12
--- /dev/null
+++ b/diacritics-macos/Base.lproj/Main.storyboard
@@ -0,0 +1,683 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/diacritics-macos/ContentView.swift b/diacritics-macos/ContentView.swift
new file mode 100644
index 0000000..93375f0
--- /dev/null
+++ b/diacritics-macos/ContentView.swift
@@ -0,0 +1,61 @@
+import SwiftUI
+import LaunchAtLogin
+
+struct ContentView: View {
+ private(set) var hotKey: HotKey?
+ private(set) var isErrored: Bool
+
+ @State
+ private var areNotificationsEnabled = StorageService.areNotificationsEnabled
+
+ @ObservedObject
+ private var launchAtLogin = LaunchAtLogin.observable
+
+ private static let columns = Array(repeating: GridItem(.flexible(), alignment: .leading), count: 2)
+
+ var body: some View {
+ VStack {
+ HStack {
+ Text("Preferences.HotKey")
+ HotKeyBinderView(
+ hotKey: hotKey,
+ isErrored: isErrored
+ )
+ .frame(maxWidth: 128, maxHeight: 26)
+ }
+ .frame(maxWidth: .infinity)
+
+ Toggle("Preferences.LaunchAtLogin", isOn: $launchAtLogin.isEnabled)
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .padding(.leading)
+
+ Toggle("Preferences.Notification", isOn: $areNotificationsEnabled)
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .padding(.leading)
+ .onChange(of: areNotificationsEnabled, perform: {
+ StorageService.areNotificationsEnabled = $0
+ })
+
+ HStack {
+ Text("Preferences.PoweredBy")
+ Link(destination: URL(string: "https://www.nechybujte.cz/nastroje")!) {
+ Image("lingea")
+ .resizable()
+ .scaledToFit()
+ .frame(height: 20)
+ .foregroundColor(Color.primary)
+ }
+ .onHover { isInside in
+ if isInside {
+ NSCursor.pointingHand.push()
+ } else {
+ NSCursor.pop()
+ }
+ }
+ }
+ .frame(maxWidth: .infinity, alignment: .trailing)
+ .padding(.top)
+ }
+ .padding()
+ }
+}
diff --git a/diacritics-macos/HotKeyBinderView.swift b/diacritics-macos/HotKeyBinderView.swift
new file mode 100644
index 0000000..fdc6043
--- /dev/null
+++ b/diacritics-macos/HotKeyBinderView.swift
@@ -0,0 +1,123 @@
+import SwiftUI
+
+struct HotKeyBinderView: View {
+ @State
+ private(set) var hotKey: HotKey?
+
+ @State
+ private(set) var isErrored: Bool = false
+
+ @State
+ private var isFocused: Bool = false
+
+ var body: some View {
+ let cornerRadius: CGFloat = 8
+
+ ZStack {
+ // Using space to not let the Text's height shrink when empty.
+ Text(hotKey?.description ?? " ")
+ .padding(4)
+ KeyEventHandling(hotKey: $hotKey, isFocused: $isFocused, isErrored: $isErrored)
+ Image(systemName: "xmark")
+ .frame(maxHeight: .infinity)
+ .padding(.horizontal, 6)
+ .background(RoundedCorners(color: .clear, strokeColor: borderColor(), tl: cornerRadius, tr: cornerRadius, bl: cornerRadius, br: cornerRadius))
+ .frame(maxWidth: .infinity, alignment: .trailing)
+ .onTapGesture {
+ hotKey = nil
+ }
+ }
+ .background(RoundedCorners(color: .white, strokeColor: borderColor(), tl: cornerRadius, tr: cornerRadius, bl: cornerRadius, br: cornerRadius))
+ .onChange(of: hotKey, perform: { newHotKey in
+ StorageService.hotKey = newHotKey
+ if let newHotKey = newHotKey {
+ isErrored = !GlobalHotKeyService.registerHotKey(hotKey: newHotKey)
+ } else {
+ isErrored = !GlobalHotKeyService.unregisterHotKey()
+ }
+ })
+ }
+
+ private func borderColor() -> Color {
+ if isErrored {
+ return .crimson
+ } else if isFocused {
+ return .blurple
+ } else {
+ return .gray
+ }
+ }
+}
+
+private struct KeyEventHandling: NSViewRepresentable {
+ private let hotKey: Binding
+ private let isFocused: Binding
+ private let isErrored: Binding
+
+ init(hotKey: Binding, isFocused: Binding, isErrored: Binding) {
+ self.hotKey = hotKey
+ self.isFocused = isFocused
+ self.isErrored = isErrored
+ }
+
+ func makeNSView(context: Context) -> NSView {
+ let view = KeyView()
+ view.delegate = context.coordinator
+ return view
+ }
+
+ func updateNSView(_ nsView: NSView, context: Context) {}
+
+ func makeCoordinator() -> Coordinator {
+ return Coordinator(self)
+ }
+
+ class KeyView: NSView {
+ weak var delegate: KeyViewDelegate?
+
+ override var acceptsFirstResponder: Bool { true }
+
+ override func keyDown(with event: NSEvent) {
+ let hotKey = HotKey(
+ keyCode: Int(event.keyCode),
+ character: event.charactersIgnoringModifiers,
+ modifiers: event.modifierFlags
+ )
+ delegate?.hotKeyChanged(hotKey: hotKey)
+ }
+
+ override func becomeFirstResponder() -> Bool {
+ let result = super.becomeFirstResponder()
+ delegate?.focusChanged(isFocused: true)
+ return result
+ }
+
+ override func resignFirstResponder() -> Bool {
+ let result = super.resignFirstResponder()
+ delegate?.focusChanged(isFocused: false)
+ return result
+ }
+ }
+
+ class Coordinator: NSObject, KeyViewDelegate {
+ var parent: KeyEventHandling
+
+ init(_ parent: KeyEventHandling) {
+ self.parent = parent
+ }
+
+ func focusChanged(isFocused: Bool) {
+ parent.isFocused.wrappedValue = isFocused
+ }
+
+ func hotKeyChanged(hotKey: HotKey) {
+ parent.hotKey.wrappedValue = hotKey
+ }
+ }
+}
+
+private protocol KeyViewDelegate: AnyObject {
+ func focusChanged(isFocused: Bool)
+
+ func hotKeyChanged(hotKey: HotKey)
+}
diff --git a/diacritics-macos/Info.plist b/diacritics-macos/Info.plist
new file mode 100644
index 0000000..c4ecb47
--- /dev/null
+++ b/diacritics-macos/Info.plist
@@ -0,0 +1,34 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ Acčento
+ CFBundlePackageType
+ $(PRODUCT_BUNDLE_PACKAGE_TYPE)
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1
+ LSApplicationCategoryType
+ public.app-category.utilities
+ LSMinimumSystemVersion
+ $(MACOSX_DEPLOYMENT_TARGET)
+ LSUIElement
+
+ NSMainStoryboardFile
+ Main
+ NSPrincipalClass
+ NSApplication
+
+
diff --git a/diacritics-macos/Preview Content/Preview Assets.xcassets/Contents.json b/diacritics-macos/Preview Content/Preview Assets.xcassets/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/diacritics-macos/Preview Content/Preview Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/diacritics-macos/Resources/cs.lproj/Localizable.strings b/diacritics-macos/Resources/cs.lproj/Localizable.strings
new file mode 100644
index 0000000..5a52610
--- /dev/null
+++ b/diacritics-macos/Resources/cs.lproj/Localizable.strings
@@ -0,0 +1,12 @@
+"Menu.Diacriticize" = "Acčento!";
+"Menu.Preferences" = "Nastavení";
+"Menu.Quit" = "Zavřít";
+
+"Notification.Title" = "Acčento!";
+"Notification.Subtitle.Uncertainties" = " %@ nejasných slov.";
+"Notification.Subtitle.Base" = "Diakritizace dokončena.%@";
+
+"Preferences.HotKey" = "Klávesová zkratka";
+"Preferences.Notification" = "Zobrazit notifikaci";
+"Preferences.LaunchAtLogin" = "Spustit po přihlášení";
+"Preferences.PoweredBy" = "umožněno díky";
diff --git a/diacritics-macos/Resources/en.lproj/Localizable.strings b/diacritics-macos/Resources/en.lproj/Localizable.strings
new file mode 100644
index 0000000..01a259e
--- /dev/null
+++ b/diacritics-macos/Resources/en.lproj/Localizable.strings
@@ -0,0 +1,12 @@
+"Menu.Diacriticize" = "Acčento!";
+"Menu.Preferences" = "Preferences";
+"Menu.Quit" = "Quit";
+
+"Notification.Title" = "Acčento!";
+"Notification.Subtitle.Uncertainties" = " %@ uncertain words.";
+"Notification.Subtitle.Base" = "Accenting complete.%@";
+
+"Preferences.HotKey" = "Hot key";
+"Preferences.Notification" = "Show notification";
+"Preferences.LaunchAtLogin" = "Launch at login";
+"Preferences.PoweredBy" = "powered by";
diff --git a/diacritics-macos/Services/DiacriticsService.swift b/diacritics-macos/Services/DiacriticsService.swift
new file mode 100644
index 0000000..21f1670
--- /dev/null
+++ b/diacritics-macos/Services/DiacriticsService.swift
@@ -0,0 +1,81 @@
+import Cocoa
+import UserNotifications
+
+final class DiacriticsService {
+ static func addDiacriticsToClipboardText() {
+ guard
+ let payload = NSPasteboard.general.string(forType: .string).map(Request.init(text:)),
+ let payloadData = try? JSONEncoder().encode(payload) else { return }
+
+ var request = URLRequest(url: URL(string: "https://www.nechybujte.cz/adddiacritics")!)
+ request.httpMethod = "POST"
+ request.setValue("Application/json", forHTTPHeaderField: "Content-Type")
+ request.httpBody = payloadData
+
+ URLSession.shared.dataTask(with: request) { (data, response, error) in
+ guard let responseString = data.flatMap({ String(data: $0, encoding: .utf8) }) else { return }
+ let cleanString = responseString
+ .replacingOccurrences(of: "", with: "")
+ .replacingOccurrences(of: "", with: "")
+ let range = NSRange(location: 0, length: cleanString.utf16.count)
+ let regex = try! NSRegularExpression(pattern: "(.*?)")
+ let matches = regex.matches(in: cleanString, options: [], range: range)
+
+ var conflicts: [Conflict] = []
+ var resultString = cleanString
+ for match in matches.reversed() {
+ let variantsRange = match.range(at: 1)
+ let wordRange = match.range(at: 2)
+
+ conflicts.append(
+ .init(
+ variants: resultString[variantsRange].components(separatedBy: ","),
+ word: String(resultString[wordRange])
+ )
+ )
+
+ resultString.removeSubrange(wordRange.upperBound.. Bool {
+ // Unregister old hotKey.
+ unregisterHotKey()
+
+ let hotKeyId = EventHotKeyID(
+ signature: OSType("swat".fourCharCodeValue),
+ id: UInt32(hotKey.keyCode)
+ )
+
+ // Register hotkey.
+ let status = RegisterEventHotKey(
+ hotKeyId.id,
+ hotKey.modifiers.carbonFlags,
+ hotKeyId,
+ GetApplicationEventTarget(),
+ 0,
+ &hotKeyRef
+ )
+
+ if status == noErr {
+ print("Successfully registered hot key '\(hotKey)'.")
+ } else {
+ print("Failed to register hot key '\(hotKey)', error: '\(status)'.")
+ }
+
+ return status == noErr
+ }
+
+ @discardableResult
+ static func unregisterHotKey() -> Bool {
+ if let hotKeyRef = hotKeyRef {
+ let status = UnregisterEventHotKey(hotKeyRef)
+ self.hotKeyRef = nil
+ return status == noErr
+ } else {
+ return true
+ }
+ }
+}
+
+private extension String {
+ /// This converts string to UInt as a fourCharCode
+ var fourCharCodeValue: Int {
+ var result: Int = 0
+ if let data = self.data(using: String.Encoding.macOSRoman) {
+ data.withUnsafeBytes({ (rawBytes) in
+ let bytes = rawBytes.bindMemory(to: UInt8.self)
+ for i in 0 ..< data.count {
+ result = result << 8 + Int(bytes[i])
+ }
+ })
+ }
+ return result
+ }
+}
+
+private extension NSEvent.ModifierFlags {
+ var carbonFlags: UInt32 {
+ let flags = rawValue
+ var newFlags: Int = 0
+
+ if ((flags & NSEvent.ModifierFlags.control.rawValue) > 0) {
+ newFlags |= controlKey
+ }
+
+ if ((flags & NSEvent.ModifierFlags.command.rawValue) > 0) {
+ newFlags |= cmdKey
+ }
+
+ if ((flags & NSEvent.ModifierFlags.shift.rawValue) > 0) {
+ newFlags |= shiftKey;
+ }
+
+ if ((flags & NSEvent.ModifierFlags.option.rawValue) > 0) {
+ newFlags |= optionKey
+ }
+
+ if ((flags & NSEvent.ModifierFlags.capsLock.rawValue) > 0) {
+ newFlags |= alphaLock
+ }
+
+ return UInt32(newFlags)
+ }
+}
diff --git a/diacritics-macos/Services/StorageService.swift b/diacritics-macos/Services/StorageService.swift
new file mode 100644
index 0000000..497230c
--- /dev/null
+++ b/diacritics-macos/Services/StorageService.swift
@@ -0,0 +1,30 @@
+import Foundation
+import Carbon
+
+final class StorageService {
+ private static let hotKeyKey = "hotKey"
+ private static let notificationsEnabledKey = "notificationsEnabled"
+
+ static var hotKey: HotKey? {
+ get {
+ UserDefaults.standard.data(forKey: Self.hotKeyKey).map { try! JSONDecoder().decode(HotKey.self, from: $0) }
+ ?? HotKey(keyCode: kVK_ANSI_D, character: "D", modifiers: [.control, .option, .command])
+ }
+ set {
+ if let newHotKey = newValue {
+ UserDefaults.standard.setValue(try! JSONEncoder().encode(newHotKey), forKey: Self.hotKeyKey)
+ } else {
+ UserDefaults.standard.removeObject(forKey: Self.hotKeyKey)
+ }
+ }
+ }
+
+ static var areNotificationsEnabled: Bool {
+ get {
+ UserDefaults.standard.bool(forKey: Self.notificationsEnabledKey)
+ }
+ set {
+ UserDefaults.standard.set(newValue, forKey: Self.notificationsEnabledKey)
+ }
+ }
+}
diff --git a/diacritics-macos/Utils/Color+assets.swift b/diacritics-macos/Utils/Color+assets.swift
new file mode 100644
index 0000000..0c44d52
--- /dev/null
+++ b/diacritics-macos/Utils/Color+assets.swift
@@ -0,0 +1,11 @@
+import SwiftUI
+
+extension Color {
+ static var blurple: Color {
+ Color("blurple")
+ }
+
+ static var crimson: Color {
+ Color("crimson")
+ }
+}
diff --git a/diacritics-macos/Utils/HotKey.swift b/diacritics-macos/Utils/HotKey.swift
new file mode 100644
index 0000000..26fe7b7
--- /dev/null
+++ b/diacritics-macos/Utils/HotKey.swift
@@ -0,0 +1,47 @@
+import Cocoa
+
+struct HotKey: Equatable {
+ let keyCode: Int
+ let character: String?
+ private let modifiersRawValue: UInt
+
+ var modifiers: NSEvent.ModifierFlags {
+ return .init(rawValue: modifiersRawValue)
+ }
+
+ init(keyCode: Int, character: String?, modifiers: NSEvent.ModifierFlags) {
+ self.keyCode = keyCode
+ self.character = character
+ self.modifiersRawValue = modifiers.rawValue
+ }
+}
+
+extension HotKey: Codable {}
+
+extension HotKey: CustomStringConvertible {
+ var description: String {
+ var stringBuilder = ""
+ if modifiers.contains(.function) {
+ stringBuilder += "fn"
+ }
+ if modifiers.contains(.control) {
+ stringBuilder += "⌃"
+ }
+ if modifiers.contains(.option) {
+ stringBuilder += "⌥"
+ }
+ if modifiers.contains(.command) {
+ stringBuilder += "⌘"
+ }
+ if modifiers.contains(.shift) {
+ stringBuilder += "⇧"
+ }
+ if modifiers.contains(.capsLock) {
+ stringBuilder += "⇪"
+ }
+ if let character = character {
+ stringBuilder += character.uppercased()
+ }
+ return stringBuilder
+ }
+}
diff --git a/diacritics-macos/Utils/RoundedCorners.swift b/diacritics-macos/Utils/RoundedCorners.swift
new file mode 100644
index 0000000..af93a5a
--- /dev/null
+++ b/diacritics-macos/Utils/RoundedCorners.swift
@@ -0,0 +1,38 @@
+import SwiftUI
+
+// Taken from https://stackoverflow.com/a/56763282/11558478
+struct RoundedCorners: View {
+ var color: Color = .blue
+ var strokeColor: Color = .gray
+ var tl: CGFloat = 0.0
+ var tr: CGFloat = 0.0
+ var bl: CGFloat = 0.0
+ var br: CGFloat = 0.0
+
+ var body: some View {
+ GeometryReader { geometry in
+ Path { path in
+ let w = geometry.size.width
+ let h = geometry.size.height
+
+ // Make sure we do not exceed the size of the rectangle
+ let tr = min(min(self.tr, h/2), w/2)
+ let tl = min(min(self.tl, h/2), w/2)
+ let bl = min(min(self.bl, h/2), w/2)
+ let br = min(min(self.br, h/2), w/2)
+
+ path.move(to: CGPoint(x: w / 2.0, y: 0))
+ path.addLine(to: CGPoint(x: w - tr, y: 0))
+ path.addArc(center: CGPoint(x: w - tr, y: tr), radius: tr, startAngle: Angle(degrees: -90), endAngle: Angle(degrees: 0), clockwise: false)
+ path.addLine(to: CGPoint(x: w, y: h - br))
+ path.addArc(center: CGPoint(x: w - br, y: h - br), radius: br, startAngle: Angle(degrees: 0), endAngle: Angle(degrees: 90), clockwise: false)
+ path.addLine(to: CGPoint(x: bl, y: h))
+ path.addArc(center: CGPoint(x: bl, y: h - bl), radius: bl, startAngle: Angle(degrees: 90), endAngle: Angle(degrees: 180), clockwise: false)
+ path.addLine(to: CGPoint(x: 0, y: tl))
+ path.addArc(center: CGPoint(x: tl, y: tl), radius: tl, startAngle: Angle(degrees: 180), endAngle: Angle(degrees: 270), clockwise: false)
+ path.closeSubpath()
+ }
+ .fill(color, strokeBorder: strokeColor, lineWidth: 1)
+ }
+ }
+}
diff --git a/diacritics-macos/Utils/Shape+extensions.swift b/diacritics-macos/Utils/Shape+extensions.swift
new file mode 100644
index 0000000..7c0f698
--- /dev/null
+++ b/diacritics-macos/Utils/Shape+extensions.swift
@@ -0,0 +1,17 @@
+import SwiftUI
+
+extension Shape {
+ func fill(_ fillStyle: Fill, strokeBorder strokeStyle: Stroke, lineWidth: CGFloat = 1) -> some View {
+ self
+ .stroke(strokeStyle, lineWidth: lineWidth)
+ .background(self.fill(fillStyle))
+ }
+}
+
+extension InsettableShape {
+ func fill(_ fillStyle: Fill, strokeBorder strokeStyle: Stroke, lineWidth: CGFloat = 1) -> some View {
+ self
+ .strokeBorder(strokeStyle, lineWidth: lineWidth)
+ .background(self.fill(fillStyle))
+ }
+}
diff --git a/diacritics-macos/Utils/String+NSRange.swift b/diacritics-macos/Utils/String+NSRange.swift
new file mode 100644
index 0000000..dbc853d
--- /dev/null
+++ b/diacritics-macos/Utils/String+NSRange.swift
@@ -0,0 +1,19 @@
+import Cocoa
+
+extension String {
+ subscript(range: NSRange) -> Substring {
+ self[
+ index(startIndex, offsetBy: range.lowerBound)
+ ..<
+ index(startIndex, offsetBy: range.upperBound)
+ ]
+ }
+
+ mutating func removeSubrange(_ range: Range) {
+ removeSubrange(
+ index(startIndex, offsetBy: range.lowerBound)
+ ..<
+ index(startIndex, offsetBy: range.upperBound)
+ )
+ }
+}
diff --git a/diacritics-macos/Utils/String+localized.swift b/diacritics-macos/Utils/String+localized.swift
new file mode 100644
index 0000000..cd30799
--- /dev/null
+++ b/diacritics-macos/Utils/String+localized.swift
@@ -0,0 +1,11 @@
+import Foundation
+
+extension String {
+ var localized: String {
+ NSLocalizedString(self, comment: "")
+ }
+
+ func localized(_ parameters: CVarArg...) -> String {
+ String(format: localized, arguments: parameters)
+ }
+}
diff --git a/diacritics-macos/diacritics_macos.entitlements b/diacritics-macos/diacritics_macos.entitlements
new file mode 100644
index 0000000..625af03
--- /dev/null
+++ b/diacritics-macos/diacritics_macos.entitlements
@@ -0,0 +1,12 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.files.user-selected.read-only
+
+ com.apple.security.network.client
+
+
+
diff --git a/img/Accento-logo.png b/img/Accento-logo.png
new file mode 100644
index 0000000..5201442
Binary files /dev/null and b/img/Accento-logo.png differ
diff --git a/img/Lingea-logo.png b/img/Lingea-logo.png
new file mode 100644
index 0000000..c3757bc
Binary files /dev/null and b/img/Lingea-logo.png differ
diff --git a/zip.sh b/zip.sh
new file mode 100755
index 0000000..61da63c
--- /dev/null
+++ b/zip.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+
+xcodebuild -scheme diacritics-macos -configuration Release -derivedDataPath "./build" clean build
+(cd build/Build/Products/Release && mv diacritics-macos.app Accento.app && zip -rX file.zip Accento.app && mv file.zip ../../../../Accento.zip)