├── .gitignore
├── LICENSE
├── README.md
├── media
└── demo.gif
├── neko.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
└── neko
├── AppDelegate.swift
├── Assets.xcassets
├── AccentColor.colorset
│ └── Contents.json
├── AppIcon.appiconset
│ └── Contents.json
├── Contents.json
└── Neko.imageset
│ ├── Contents.json
│ ├── sprites-1.png
│ ├── sprites-2.png
│ └── sprites.png
├── Base.lproj
└── Main.storyboard
├── ContentView.swift
├── Info.plist
├── Neko.swift
├── NekoAnimation.swift
├── Preview Content
└── Preview Assets.xcassets
│ └── Contents.json
├── Store.swift
└── neko.entitlements
/.gitignore:
--------------------------------------------------------------------------------
1 | xcuserdata/
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2020 Bogdan Popa
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # neko.app
2 |
3 | A tiny kitten that follows your mouse on macOS.
4 |
5 |
6 |
7 |
8 |
9 | Sprites taken from [skiftOS].
10 |
11 | [skiftOS]: https://github.com/skiftOS/skift
12 |
--------------------------------------------------------------------------------
/media/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Bogdanp/neko/5874f8217ddc8fb4c83df6c465e38d0fd5dba7bd/media/demo.gif
--------------------------------------------------------------------------------
/neko.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 43F8AE81259F5CF500092212 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F8AE80259F5CF500092212 /* AppDelegate.swift */; };
11 | 43F8AE83259F5CF500092212 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F8AE82259F5CF500092212 /* ContentView.swift */; };
12 | 43F8AE85259F5CF600092212 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 43F8AE84259F5CF600092212 /* Assets.xcassets */; };
13 | 43F8AE88259F5CF600092212 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 43F8AE87259F5CF600092212 /* Preview Assets.xcassets */; };
14 | 43F8AE8B259F5CF600092212 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 43F8AE89259F5CF600092212 /* Main.storyboard */; };
15 | 43F8AE98259F5EC200092212 /* Neko.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F8AE97259F5EC200092212 /* Neko.swift */; };
16 | 43F8AE9D259F6F0000092212 /* NekoAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F8AE9C259F6F0000092212 /* NekoAnimation.swift */; };
17 | 43F8AEA3259F843200092212 /* Store.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F8AEA2259F843200092212 /* Store.swift */; };
18 | /* End PBXBuildFile section */
19 |
20 | /* Begin PBXFileReference section */
21 | 43F8AE7D259F5CF500092212 /* neko.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = neko.app; sourceTree = BUILT_PRODUCTS_DIR; };
22 | 43F8AE80259F5CF500092212 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
23 | 43F8AE82259F5CF500092212 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
24 | 43F8AE84259F5CF600092212 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
25 | 43F8AE87259F5CF600092212 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
26 | 43F8AE8A259F5CF600092212 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
27 | 43F8AE8C259F5CF600092212 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
28 | 43F8AE8D259F5CF600092212 /* neko.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = neko.entitlements; sourceTree = ""; };
29 | 43F8AE97259F5EC200092212 /* Neko.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Neko.swift; sourceTree = ""; };
30 | 43F8AE9C259F6F0000092212 /* NekoAnimation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NekoAnimation.swift; sourceTree = ""; };
31 | 43F8AEA2259F843200092212 /* Store.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Store.swift; sourceTree = ""; };
32 | /* End PBXFileReference section */
33 |
34 | /* Begin PBXFrameworksBuildPhase section */
35 | 43F8AE7A259F5CF500092212 /* Frameworks */ = {
36 | isa = PBXFrameworksBuildPhase;
37 | buildActionMask = 2147483647;
38 | files = (
39 | );
40 | runOnlyForDeploymentPostprocessing = 0;
41 | };
42 | /* End PBXFrameworksBuildPhase section */
43 |
44 | /* Begin PBXGroup section */
45 | 43F8AE74259F5CF500092212 = {
46 | isa = PBXGroup;
47 | children = (
48 | 43F8AE7F259F5CF500092212 /* neko */,
49 | 43F8AE7E259F5CF500092212 /* Products */,
50 | );
51 | sourceTree = "";
52 | };
53 | 43F8AE7E259F5CF500092212 /* Products */ = {
54 | isa = PBXGroup;
55 | children = (
56 | 43F8AE7D259F5CF500092212 /* neko.app */,
57 | );
58 | name = Products;
59 | sourceTree = "";
60 | };
61 | 43F8AE7F259F5CF500092212 /* neko */ = {
62 | isa = PBXGroup;
63 | children = (
64 | 43F8AE80259F5CF500092212 /* AppDelegate.swift */,
65 | 43F8AE84259F5CF600092212 /* Assets.xcassets */,
66 | 43F8AE82259F5CF500092212 /* ContentView.swift */,
67 | 43F8AE8C259F5CF600092212 /* Info.plist */,
68 | 43F8AE89259F5CF600092212 /* Main.storyboard */,
69 | 43F8AE8D259F5CF600092212 /* neko.entitlements */,
70 | 43F8AE97259F5EC200092212 /* Neko.swift */,
71 | 43F8AE9C259F6F0000092212 /* NekoAnimation.swift */,
72 | 43F8AE86259F5CF600092212 /* Preview Content */,
73 | 43F8AEA2259F843200092212 /* Store.swift */,
74 | );
75 | path = neko;
76 | sourceTree = "";
77 | };
78 | 43F8AE86259F5CF600092212 /* Preview Content */ = {
79 | isa = PBXGroup;
80 | children = (
81 | 43F8AE87259F5CF600092212 /* Preview Assets.xcassets */,
82 | );
83 | path = "Preview Content";
84 | sourceTree = "";
85 | };
86 | /* End PBXGroup section */
87 |
88 | /* Begin PBXNativeTarget section */
89 | 43F8AE7C259F5CF500092212 /* neko */ = {
90 | isa = PBXNativeTarget;
91 | buildConfigurationList = 43F8AE90259F5CF600092212 /* Build configuration list for PBXNativeTarget "neko" */;
92 | buildPhases = (
93 | 43F8AE79259F5CF500092212 /* Sources */,
94 | 43F8AE7A259F5CF500092212 /* Frameworks */,
95 | 43F8AE7B259F5CF500092212 /* Resources */,
96 | );
97 | buildRules = (
98 | );
99 | dependencies = (
100 | );
101 | name = neko;
102 | productName = neko;
103 | productReference = 43F8AE7D259F5CF500092212 /* neko.app */;
104 | productType = "com.apple.product-type.application";
105 | };
106 | /* End PBXNativeTarget section */
107 |
108 | /* Begin PBXProject section */
109 | 43F8AE75259F5CF500092212 /* Project object */ = {
110 | isa = PBXProject;
111 | attributes = {
112 | LastSwiftUpdateCheck = 1220;
113 | LastUpgradeCheck = 1220;
114 | TargetAttributes = {
115 | 43F8AE7C259F5CF500092212 = {
116 | CreatedOnToolsVersion = 12.2;
117 | };
118 | };
119 | };
120 | buildConfigurationList = 43F8AE78259F5CF500092212 /* Build configuration list for PBXProject "neko" */;
121 | compatibilityVersion = "Xcode 9.3";
122 | developmentRegion = en;
123 | hasScannedForEncodings = 0;
124 | knownRegions = (
125 | en,
126 | Base,
127 | );
128 | mainGroup = 43F8AE74259F5CF500092212;
129 | productRefGroup = 43F8AE7E259F5CF500092212 /* Products */;
130 | projectDirPath = "";
131 | projectRoot = "";
132 | targets = (
133 | 43F8AE7C259F5CF500092212 /* neko */,
134 | );
135 | };
136 | /* End PBXProject section */
137 |
138 | /* Begin PBXResourcesBuildPhase section */
139 | 43F8AE7B259F5CF500092212 /* Resources */ = {
140 | isa = PBXResourcesBuildPhase;
141 | buildActionMask = 2147483647;
142 | files = (
143 | 43F8AE8B259F5CF600092212 /* Main.storyboard in Resources */,
144 | 43F8AE88259F5CF600092212 /* Preview Assets.xcassets in Resources */,
145 | 43F8AE85259F5CF600092212 /* Assets.xcassets in Resources */,
146 | );
147 | runOnlyForDeploymentPostprocessing = 0;
148 | };
149 | /* End PBXResourcesBuildPhase section */
150 |
151 | /* Begin PBXSourcesBuildPhase section */
152 | 43F8AE79259F5CF500092212 /* Sources */ = {
153 | isa = PBXSourcesBuildPhase;
154 | buildActionMask = 2147483647;
155 | files = (
156 | 43F8AEA3259F843200092212 /* Store.swift in Sources */,
157 | 43F8AE83259F5CF500092212 /* ContentView.swift in Sources */,
158 | 43F8AE98259F5EC200092212 /* Neko.swift in Sources */,
159 | 43F8AE81259F5CF500092212 /* AppDelegate.swift in Sources */,
160 | 43F8AE9D259F6F0000092212 /* NekoAnimation.swift in Sources */,
161 | );
162 | runOnlyForDeploymentPostprocessing = 0;
163 | };
164 | /* End PBXSourcesBuildPhase section */
165 |
166 | /* Begin PBXVariantGroup section */
167 | 43F8AE89259F5CF600092212 /* Main.storyboard */ = {
168 | isa = PBXVariantGroup;
169 | children = (
170 | 43F8AE8A259F5CF600092212 /* Base */,
171 | );
172 | name = Main.storyboard;
173 | sourceTree = "";
174 | };
175 | /* End PBXVariantGroup section */
176 |
177 | /* Begin XCBuildConfiguration section */
178 | 43F8AE8E259F5CF600092212 /* Debug */ = {
179 | isa = XCBuildConfiguration;
180 | buildSettings = {
181 | ALWAYS_SEARCH_USER_PATHS = NO;
182 | CLANG_ANALYZER_NONNULL = YES;
183 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
184 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
185 | CLANG_CXX_LIBRARY = "libc++";
186 | CLANG_ENABLE_MODULES = YES;
187 | CLANG_ENABLE_OBJC_ARC = YES;
188 | CLANG_ENABLE_OBJC_WEAK = YES;
189 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
190 | CLANG_WARN_BOOL_CONVERSION = YES;
191 | CLANG_WARN_COMMA = YES;
192 | CLANG_WARN_CONSTANT_CONVERSION = YES;
193 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
194 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
195 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
196 | CLANG_WARN_EMPTY_BODY = YES;
197 | CLANG_WARN_ENUM_CONVERSION = YES;
198 | CLANG_WARN_INFINITE_RECURSION = YES;
199 | CLANG_WARN_INT_CONVERSION = YES;
200 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
201 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
202 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
203 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
204 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
205 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
206 | CLANG_WARN_STRICT_PROTOTYPES = YES;
207 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
208 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
209 | CLANG_WARN_UNREACHABLE_CODE = YES;
210 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
211 | COPY_PHASE_STRIP = NO;
212 | DEBUG_INFORMATION_FORMAT = dwarf;
213 | ENABLE_STRICT_OBJC_MSGSEND = YES;
214 | ENABLE_TESTABILITY = YES;
215 | GCC_C_LANGUAGE_STANDARD = gnu11;
216 | GCC_DYNAMIC_NO_PIC = NO;
217 | GCC_NO_COMMON_BLOCKS = YES;
218 | GCC_OPTIMIZATION_LEVEL = 0;
219 | GCC_PREPROCESSOR_DEFINITIONS = (
220 | "DEBUG=1",
221 | "$(inherited)",
222 | );
223 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
224 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
225 | GCC_WARN_UNDECLARED_SELECTOR = YES;
226 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
227 | GCC_WARN_UNUSED_FUNCTION = YES;
228 | GCC_WARN_UNUSED_VARIABLE = YES;
229 | MACOSX_DEPLOYMENT_TARGET = 10.15;
230 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
231 | MTL_FAST_MATH = YES;
232 | ONLY_ACTIVE_ARCH = YES;
233 | SDKROOT = macosx;
234 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
235 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
236 | };
237 | name = Debug;
238 | };
239 | 43F8AE8F259F5CF600092212 /* Release */ = {
240 | isa = XCBuildConfiguration;
241 | buildSettings = {
242 | ALWAYS_SEARCH_USER_PATHS = NO;
243 | CLANG_ANALYZER_NONNULL = YES;
244 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
245 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
246 | CLANG_CXX_LIBRARY = "libc++";
247 | CLANG_ENABLE_MODULES = YES;
248 | CLANG_ENABLE_OBJC_ARC = YES;
249 | CLANG_ENABLE_OBJC_WEAK = YES;
250 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
251 | CLANG_WARN_BOOL_CONVERSION = YES;
252 | CLANG_WARN_COMMA = YES;
253 | CLANG_WARN_CONSTANT_CONVERSION = YES;
254 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
255 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
256 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
257 | CLANG_WARN_EMPTY_BODY = YES;
258 | CLANG_WARN_ENUM_CONVERSION = YES;
259 | CLANG_WARN_INFINITE_RECURSION = YES;
260 | CLANG_WARN_INT_CONVERSION = YES;
261 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
262 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
263 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
264 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
265 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
266 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
267 | CLANG_WARN_STRICT_PROTOTYPES = YES;
268 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
269 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
270 | CLANG_WARN_UNREACHABLE_CODE = YES;
271 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
272 | COPY_PHASE_STRIP = NO;
273 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
274 | ENABLE_NS_ASSERTIONS = NO;
275 | ENABLE_STRICT_OBJC_MSGSEND = YES;
276 | GCC_C_LANGUAGE_STANDARD = gnu11;
277 | GCC_NO_COMMON_BLOCKS = YES;
278 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
279 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
280 | GCC_WARN_UNDECLARED_SELECTOR = YES;
281 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
282 | GCC_WARN_UNUSED_FUNCTION = YES;
283 | GCC_WARN_UNUSED_VARIABLE = YES;
284 | MACOSX_DEPLOYMENT_TARGET = 10.15;
285 | MTL_ENABLE_DEBUG_INFO = NO;
286 | MTL_FAST_MATH = YES;
287 | SDKROOT = macosx;
288 | SWIFT_COMPILATION_MODE = wholemodule;
289 | SWIFT_OPTIMIZATION_LEVEL = "-O";
290 | };
291 | name = Release;
292 | };
293 | 43F8AE91259F5CF600092212 /* Debug */ = {
294 | isa = XCBuildConfiguration;
295 | buildSettings = {
296 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
297 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
298 | CODE_SIGN_ENTITLEMENTS = neko/neko.entitlements;
299 | CODE_SIGN_STYLE = Automatic;
300 | COMBINE_HIDPI_IMAGES = YES;
301 | DEVELOPMENT_ASSET_PATHS = "\"neko/Preview Content\"";
302 | DEVELOPMENT_TEAM = H3YE679B58;
303 | ENABLE_HARDENED_RUNTIME = YES;
304 | ENABLE_PREVIEWS = YES;
305 | INFOPLIST_FILE = neko/Info.plist;
306 | LD_RUNPATH_SEARCH_PATHS = (
307 | "$(inherited)",
308 | "@executable_path/../Frameworks",
309 | );
310 | MACOSX_DEPLOYMENT_TARGET = 10.15;
311 | MARKETING_VERSION = 2021.01.02;
312 | PRODUCT_BUNDLE_IDENTIFIER = io.defn.neko;
313 | PRODUCT_NAME = "$(TARGET_NAME)";
314 | SWIFT_VERSION = 5.0;
315 | };
316 | name = Debug;
317 | };
318 | 43F8AE92259F5CF600092212 /* Release */ = {
319 | isa = XCBuildConfiguration;
320 | buildSettings = {
321 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
322 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
323 | CODE_SIGN_ENTITLEMENTS = neko/neko.entitlements;
324 | CODE_SIGN_STYLE = Automatic;
325 | COMBINE_HIDPI_IMAGES = YES;
326 | DEVELOPMENT_ASSET_PATHS = "\"neko/Preview Content\"";
327 | DEVELOPMENT_TEAM = H3YE679B58;
328 | ENABLE_HARDENED_RUNTIME = YES;
329 | ENABLE_PREVIEWS = YES;
330 | INFOPLIST_FILE = neko/Info.plist;
331 | LD_RUNPATH_SEARCH_PATHS = (
332 | "$(inherited)",
333 | "@executable_path/../Frameworks",
334 | );
335 | MACOSX_DEPLOYMENT_TARGET = 10.15;
336 | MARKETING_VERSION = 2021.01.02;
337 | PRODUCT_BUNDLE_IDENTIFIER = io.defn.neko;
338 | PRODUCT_NAME = "$(TARGET_NAME)";
339 | SWIFT_VERSION = 5.0;
340 | };
341 | name = Release;
342 | };
343 | /* End XCBuildConfiguration section */
344 |
345 | /* Begin XCConfigurationList section */
346 | 43F8AE78259F5CF500092212 /* Build configuration list for PBXProject "neko" */ = {
347 | isa = XCConfigurationList;
348 | buildConfigurations = (
349 | 43F8AE8E259F5CF600092212 /* Debug */,
350 | 43F8AE8F259F5CF600092212 /* Release */,
351 | );
352 | defaultConfigurationIsVisible = 0;
353 | defaultConfigurationName = Release;
354 | };
355 | 43F8AE90259F5CF600092212 /* Build configuration list for PBXNativeTarget "neko" */ = {
356 | isa = XCConfigurationList;
357 | buildConfigurations = (
358 | 43F8AE91259F5CF600092212 /* Debug */,
359 | 43F8AE92259F5CF600092212 /* Release */,
360 | );
361 | defaultConfigurationIsVisible = 0;
362 | defaultConfigurationName = Release;
363 | };
364 | /* End XCConfigurationList section */
365 | };
366 | rootObject = 43F8AE75259F5CF500092212 /* Project object */;
367 | }
368 |
--------------------------------------------------------------------------------
/neko.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/neko.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/neko/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import Cocoa
2 | import SwiftUI
3 |
4 | @main
5 | class AppDelegate: NSObject, NSApplicationDelegate {
6 | var window: NSWindow!
7 |
8 | func applicationDidFinishLaunching(_ aNotification: Notification) {
9 | window = NSWindow(
10 | contentRect: NSRect(x: 0, y: 0, width: 16, height: 16),
11 | styleMask: [.borderless],
12 | backing: .buffered,
13 | defer: false)
14 | window.level = NSWindow.Level.mainMenu
15 | window.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
16 | window.isReleasedWhenClosed = false
17 | window.ignoresMouseEvents = true
18 | window.center()
19 |
20 | let store = Store(withMouseLoc: NSEvent.mouseLocation, andNekoLoc: window.frame.origin)
21 | let contentView = ContentView(store: store)
22 | let hostingView = NSHostingView(rootView: contentView)
23 | Timer.scheduledTimer(withTimeInterval: 0.16, repeats: true) { _ in
24 | DispatchQueue.main.async {
25 | self.window.setFrameOrigin(store.nextTick(NSEvent.mouseLocation))
26 | }
27 | }
28 |
29 | window.backgroundColor = NSColor.init(calibratedRed: 1, green: 1, blue: 1, alpha: 0)
30 | window.contentView = hostingView
31 | window.makeKeyAndOrderFront(nil)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/neko/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/neko/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "scale" : "1x",
6 | "size" : "16x16"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "scale" : "2x",
11 | "size" : "16x16"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "scale" : "1x",
16 | "size" : "32x32"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "scale" : "2x",
21 | "size" : "32x32"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "scale" : "1x",
26 | "size" : "128x128"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "scale" : "2x",
31 | "size" : "128x128"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "scale" : "1x",
36 | "size" : "256x256"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "scale" : "2x",
41 | "size" : "256x256"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "scale" : "1x",
46 | "size" : "512x512"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "scale" : "2x",
51 | "size" : "512x512"
52 | }
53 | ],
54 | "info" : {
55 | "author" : "xcode",
56 | "version" : 1
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/neko/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/neko/Assets.xcassets/Neko.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "sprites.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "filename" : "sprites-1.png",
10 | "idiom" : "universal",
11 | "scale" : "2x"
12 | },
13 | {
14 | "filename" : "sprites-2.png",
15 | "idiom" : "universal",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "author" : "xcode",
21 | "version" : 1
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/neko/Assets.xcassets/Neko.imageset/sprites-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Bogdanp/neko/5874f8217ddc8fb4c83df6c465e38d0fd5dba7bd/neko/Assets.xcassets/Neko.imageset/sprites-1.png
--------------------------------------------------------------------------------
/neko/Assets.xcassets/Neko.imageset/sprites-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Bogdanp/neko/5874f8217ddc8fb4c83df6c465e38d0fd5dba7bd/neko/Assets.xcassets/Neko.imageset/sprites-2.png
--------------------------------------------------------------------------------
/neko/Assets.xcassets/Neko.imageset/sprites.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Bogdanp/neko/5874f8217ddc8fb4c83df6c465e38d0fd5dba7bd/neko/Assets.xcassets/Neko.imageset/sprites.png
--------------------------------------------------------------------------------
/neko/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/neko/ContentView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct ContentView: View {
4 | @ObservedObject var store: Store
5 |
6 | var body: some View {
7 | NekoAnimation(
8 | animation: $store.anim,
9 | tick: $store.tick
10 | )
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/neko/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
19 | CFBundleShortVersionString
20 | $(MARKETING_VERSION)
21 | CFBundleVersion
22 | 1
23 | LSApplicationCategoryType
24 | public.app-category.games
25 | LSMinimumSystemVersion
26 | $(MACOSX_DEPLOYMENT_TARGET)
27 | NSMainStoryboardFile
28 | Main
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/neko/Neko.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | enum NekoState {
4 | case idle
5 | case alert
6 | case thinking
7 | case grooming1
8 | case grooming2
9 | case yawning
10 | case sleeping1
11 | case sleeping2
12 | case movingNorthWest1
13 | case movingNorthWest2
14 | case movingNorth1
15 | case movingNorth2
16 | case movingNorthEast1
17 | case movingNorthEast2
18 | case movingEast1
19 | case movingEast2
20 | case movingSouthEast1
21 | case movingSouthEast2
22 | case movingSouth1
23 | case movingSouth2
24 | case movingSouthWest1
25 | case movingSouthWest2
26 | case movingWest1
27 | case movingWest2
28 | }
29 |
30 | struct Neko: View {
31 | @Binding var state: NekoState
32 |
33 | var body: some View {
34 | Image("Neko")
35 | .frame(width: 16, height: 16, alignment: .topLeading)
36 | .offset(x: offset.x, y: offset.y)
37 | }
38 |
39 | private func sprite(_ col: Int, _ row: Int) -> NSPoint {
40 | return NSPoint(x: -1 * row * 16, y: -1 * col * 16)
41 | }
42 |
43 | private var offset: CGPoint {
44 | get {
45 | switch state {
46 | case .idle:
47 | return sprite(0, 4)
48 |
49 | case .alert:
50 | return sprite(0, 0)
51 |
52 | case .thinking:
53 | return sprite(0, 3)
54 |
55 | case .grooming1:
56 | return sprite(1, 3)
57 | case .grooming2:
58 | return sprite(2, 3)
59 |
60 | case .yawning:
61 | return sprite(1, 4)
62 |
63 | case .sleeping1:
64 | return sprite(4, 3)
65 | case .sleeping2:
66 | return sprite(4, 2)
67 |
68 | case .movingNorthWest1:
69 | return sprite(1, 5)
70 | case .movingNorthWest2:
71 | return sprite(2, 5)
72 |
73 | case .movingNorth1:
74 | return sprite(4, 4)
75 | case .movingNorth2:
76 | return sprite(0, 5)
77 |
78 | case .movingNorthEast1:
79 | return sprite(3, 5)
80 | case .movingNorthEast2:
81 | return sprite(4, 5)
82 |
83 | case .movingEast1:
84 | return sprite(3, 4)
85 | case .movingEast2:
86 | return sprite(2, 4)
87 |
88 | case .movingSouthEast1:
89 | return sprite(2, 1)
90 | case .movingSouthEast2:
91 | return sprite(2, 2)
92 |
93 | case .movingSouth1:
94 | return sprite(0, 1)
95 | case .movingSouth2:
96 | return sprite(1, 0)
97 |
98 | case .movingSouthWest1:
99 | return sprite(1, 2)
100 | case .movingSouthWest2:
101 | return sprite(2, 0)
102 |
103 | case .movingWest1:
104 | return sprite(3, 1)
105 | case .movingWest2:
106 | return sprite(3, 0)
107 | }
108 | }
109 | }
110 | }
111 |
112 | struct Neko_Previews: PreviewProvider {
113 | static var previews: some View {
114 | Neko(state: Binding.constant(.movingSouth2))
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/neko/NekoAnimation.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct NekoAnimation: View {
4 | @Binding var animation: [NekoState]
5 | @Binding var tick: Int
6 |
7 | var body: some View {
8 | Neko(state: $animation[tick % animation.count])
9 | }
10 | }
11 |
12 | struct NekoAnimation_Previews: PreviewProvider {
13 | static var previews: some View {
14 | NekoAnimation(
15 | animation: Binding.constant([.sleeping1, .sleeping2]),
16 | tick: Binding.constant(0)
17 | )
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/neko/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/neko/Store.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | enum Direction {
4 | case none
5 | case northWest
6 | case north
7 | case northEast
8 | case east
9 | case southEast
10 | case south
11 | case southWest
12 | case west
13 | }
14 |
15 | final class Store: ObservableObject {
16 | private var direction: Direction
17 | private var ticksSinceLastMove = 0
18 |
19 | @Published var nekoLoc: NSPoint = NSPoint(x: 0, y: 0)
20 | @Published var mouseLoc: NSPoint = NSPoint(x: 0, y: 0)
21 | @Published var tick: Int = 0
22 | @Published var anim: [NekoState] = [.idle]
23 |
24 | init(withMouseLoc mouseLoc: NSPoint, andNekoLoc nekoLoc: NSPoint) {
25 | self.mouseLoc = mouseLoc
26 | self.nekoLoc = nekoLoc
27 | self.direction = nextDirection(mouseLoc, nekoLoc)
28 | }
29 |
30 | func nextTick(_ newMouseLoc: NSPoint) -> NSPoint {
31 | tick += 1
32 |
33 | let newDirection = nextDirection(newMouseLoc, nekoLoc)
34 | if direction != newDirection {
35 | tick = 0
36 | if direction == .none {
37 | anim = [.alert]
38 | } else {
39 | anim = [.idle]
40 | }
41 | direction = newDirection
42 | ticksSinceLastMove = 0
43 | return nekoLoc
44 | }
45 |
46 | if mouseLoc == newMouseLoc {
47 | ticksSinceLastMove += 1
48 | }
49 |
50 | switch direction {
51 | case .none:
52 | anim = [.idle]
53 |
54 | if ticksSinceLastMove > 33 {
55 | anim = [.sleeping1, .sleeping1, .sleeping1, .sleeping1, .sleeping2, .sleeping2, .sleeping2, .sleeping2]
56 | } else if ticksSinceLastMove > 31 {
57 | anim = [.yawning, .yawning]
58 | } else if ticksSinceLastMove > 16 {
59 | anim = [.idle]
60 | } else if ticksSinceLastMove > 8 {
61 | anim = [.grooming1, .grooming2]
62 | }
63 | case .northWest:
64 | anim = [.movingNorthWest1, .movingNorthWest2]
65 | nekoLoc = move(-1, 1)
66 | case .north:
67 | anim = [.movingNorth1, .movingNorth2]
68 | nekoLoc = move(0, 1)
69 | case .northEast:
70 | anim = [.movingNorthEast1, .movingNorthEast2]
71 | nekoLoc = move(1, 1)
72 | case .east:
73 | anim = [.movingEast1, .movingEast2]
74 | nekoLoc = move(1, 0)
75 | case .southEast:
76 | anim = [.movingSouthEast1, .movingSouthEast2]
77 | nekoLoc = move(1, -1)
78 | case .south:
79 | anim = [.movingSouth1, .movingSouth2]
80 | nekoLoc = move(0, -1)
81 | case .southWest:
82 | anim = [.movingSouthWest1, .movingSouthWest2]
83 | nekoLoc = move(-1, -1)
84 | case .west:
85 | anim = [.movingWest1, .movingWest2]
86 | nekoLoc = move(-1, 0)
87 | }
88 |
89 | mouseLoc = newMouseLoc
90 | return nekoLoc
91 | }
92 |
93 | private func move(_ xSteps: CGFloat, _ ySteps: CGFloat) -> NSPoint {
94 | return NSPoint(x: nekoLoc.x + CGFloat(step) * xSteps, y: nekoLoc.y + CGFloat(step) * ySteps)
95 | }
96 | }
97 |
98 | private let step: CGFloat = 16
99 |
100 | private func nextDirection(_ mouseLoc: NSPoint, _ nekoLoc: NSPoint) -> Direction {
101 | let d = delta(nekoLoc, mouseLoc)
102 | if d.x >= 1 {
103 | if d.y > -1 && d.y < 1 {
104 | return .west
105 | } else if d.y >= 1 {
106 | return .southWest
107 | } else {
108 | return .northWest
109 | }
110 | } else if d.x <= -1 {
111 | if d.y > -1 && d.y < 1 {
112 | return .east
113 | } else if d.y >= 1 {
114 | return .southEast
115 | } else {
116 | return .northEast
117 | }
118 | } else {
119 | if d.y > -1 && d.y < 1{
120 | return .none
121 | } else if d.y >= 1 {
122 | return .south
123 | } else {
124 | return .north
125 | }
126 | }
127 | }
128 |
129 | private func delta(_ p1: NSPoint, _ p2: NSPoint) -> NSPoint {
130 | return NSPoint(x: (p1.x - p2.x) / step, y: (p1.y - p2.y) / step)
131 | }
132 |
--------------------------------------------------------------------------------
/neko/neko.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------