@ondeinference / klepon / commits / 28d6a89

Add macOS app target

Seto Elkahfi committed May 15, 2026 at 07:36 UTC 28d6a89c904b81b1d95856b643a63044ace4fb16
17 files changed +591 -40
.github/workflows/ci.yml
+25
@@ -33,3 +33,28 @@ jobs:
33 -destination 'platform=iOS Simulator,OS=26.2,name=iPhone 17' \
34 -only-testing:KleponSnapshotTests \
35 CODE_SIGNING_ALLOWED=NO
36 +
37 + macos-build:
38 + name: macOS app build
39 + runs-on: macos-26
40 +
41 + steps:
42 + - name: Checkout
43 + uses: actions/checkout@v6
44 +
45 + - name: Install XcodeGen
46 + run: brew install xcodegen
47 +
48 + - name: Generate project
49 + run: xcodegen generate
50 +
51 + - name: Resolve Swift packages
52 + run: xcodebuild -project Klepon.xcodeproj -scheme 'Klepon Mac' -resolvePackageDependencies
53 +
54 + - name: Build macOS app
55 + run: |
56 + xcodebuild build \
57 + -project Klepon.xcodeproj \
58 + -scheme 'Klepon Mac' \
59 + -destination 'platform=macOS' \
60 + CODE_SIGNING_ALLOWED=NO
Klepon.xcodeproj/project.pbxproj
+258
@@ -8,49 +8,90 @@
8
9 /* Begin PBXBuildFile section */
10 019B658F26ABE2706231EC9E /* GuideEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0A0935AE4B398B281758834 /* GuideEntry.swift */; };
11 + 032AFB30CAB4E8A31CDC897D /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7E886BAB66E828950DBED52 /* SearchView.swift */; };
12 03D16A2386EB68FED8D92D52 /* GuideEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0A0935AE4B398B281758834 /* GuideEntry.swift */; };
13 05BF0404B7030EDCD3D8C357 /* GuideArtworkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F2850226B4B05FF758C1F93 /* GuideArtworkView.swift */; };
14 + 0709F0B92923D97F6859275A /* SearchService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6235AD6792F25476E2A508F0 /* SearchService.swift */; };
15 0977CD6BFFDBDBE3B7795924 /* collections.json in Resources */ = {isa = PBXBuildFile; fileRef = 1FDDB0F88FF430ABD679AD89 /* collections.json */; };
16 0CE2632DF6FB02163D55BB65 /* Klepon Watch.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 9E76510CC840CE068B0FC381 /* Klepon Watch.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
17 0D685E35EE50C926DC6FBE4C /* KleponChip.swift in Sources */ = {isa = PBXBuildFile; fileRef = B83C4F3C8688072191AA16C6 /* KleponChip.swift */; };
18 0E69E79C5FADF1633FE3D63F /* KleponColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C675ABBBA25BBA25A8DE29BE /* KleponColor.swift */; };
19 14B7CE6E6A0EB12B709F7B55 /* KleponTypography.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD520C870387E82E9A1533C2 /* KleponTypography.swift */; };
20 1704DDB60B1E23057C0E1094 /* GuideAnswerService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3719031080B59898133810F7 /* GuideAnswerService.swift */; };
21 + 1879460ECC35F3386E8E6109 /* KleponChip.swift in Sources */ = {isa = PBXBuildFile; fileRef = B83C4F3C8688072191AA16C6 /* KleponChip.swift */; };
22 19E282A6347C588E17778195 /* DiscoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4545DC84863788D55B94D31A /* DiscoverView.swift */; };
23 1E7AAEA8007BFE4ED6FE4892 /* WatchContentLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C77AF5EB4B17D087FA522F6 /* WatchContentLoader.swift */; };
24 1F95A454A641C829BD304A89 /* GuideCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = F17C0DB3BD620AD9F55B7631 /* GuideCollection.swift */; };
25 1FF490E985C31BB08DA29AFF /* guide_entries.json in Resources */ = {isa = PBXBuildFile; fileRef = 248F359109A08A60F792C42D /* guide_entries.json */; };
26 200AE94E7E7E73B5C83869DD /* FavoritesStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BC0C4C22A5134DB058E41F4 /* FavoritesStore.swift */; };
27 + 215C4D8A2B2ABF2C3C11C163 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8870F09A11EA6D3F0108DA4D /* SystemConfiguration.framework */; };
28 23E22EC56BE48A20BD1229F7 /* AnswerCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = B02B0EA4B8BA09604D62A62B /* AnswerCard.swift */; };
29 2AB57009245E1BABD1156AFA /* GuideDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7541983FF7CE190C0663294D /* GuideDetailView.swift */; };
30 + 2AD05B0B7ED109134F013862 /* EmptyStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E7B5E3D9A1906C2FED3032 /* EmptyStateView.swift */; };
31 + 2F3047F5078C4526133CB848 /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13418896CA28250CEEE7592E /* OnboardingView.swift */; };
32 3349FB46691C944112401D6E /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42E152344B39D1D77F1B3943 /* SettingsView.swift */; };
33 + 35B5508906432B3EFE26F4FF /* PlatformViewSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = F51D6F97615398D511673B98 /* PlatformViewSupport.swift */; };
34 + 35D43509610D0E2CB3DBD2BB /* testEmptyStateView.saved-empty-state.png in Resources */ = {isa = PBXBuildFile; fileRef = 943670F01925DE3065B92636 /* testEmptyStateView.saved-empty-state.png */; };
35 36AA193A3B7EF7CB25B6E0BE /* KleponWatchApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EFBF98118F066D3C7FA14B2 /* KleponWatchApp.swift */; };
36 + 37A723A15D19B7DE3DD92B25 /* PlatformViewSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = F51D6F97615398D511673B98 /* PlatformViewSupport.swift */; };
37 + 37BD5B2A275426143819B70D /* OndeGuideEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0D53A5F9D5897E15506C20 /* OndeGuideEngine.swift */; };
38 + 3A6F67743964C4D04893C321 /* RecentlyViewedStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E45070309D2C7058BAA44CD2 /* RecentlyViewedStore.swift */; };
39 + 3F999E04650931C2019BDA17 /* KleponColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C675ABBBA25BBA25A8DE29BE /* KleponColor.swift */; };
40 41F8E173B82809AF87E1B297 /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13418896CA28250CEEE7592E /* OnboardingView.swift */; };
41 + 43ABAA3C08695B7C8FEE7324 /* KleponMetadataRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7E1C50BFBE2EF5F37941590 /* KleponMetadataRow.swift */; };
42 4609C70D9FB74C55B9DBE832 /* KleponActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52DD909BB2EF68103CB46EDA /* KleponActionButton.swift */; };
43 478FFEBDAA7729189B429A6D /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7E886BAB66E828950DBED52 /* SearchView.swift */; };
44 + 47B632AF6BF3E25AF628CC01 /* Onde in Frameworks */ = {isa = PBXBuildFile; productRef = 35BDA28E3F43D5441C5FE932 /* Onde */; };
45 4D63E231AC9630B2C225DF22 /* WatchHomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDBB4F5D4C98D036AC52E193 /* WatchHomeView.swift */; };
46 + 5692BBCE950885C5C1193DDF /* collections.json in Resources */ = {isa = PBXBuildFile; fileRef = 1FDDB0F88FF430ABD679AD89 /* collections.json */; };
47 + 5808C4539EBB98D822BDE99F /* KleponActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52DD909BB2EF68103CB46EDA /* KleponActionButton.swift */; };
48 67368394088AA3045EBA14A5 /* OndeEnvironmentBootstrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B499D3BF7AC80D31907D87F /* OndeEnvironmentBootstrap.swift */; };
49 + 687418866BCC5554A0F4BFDC /* AnswerCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = B02B0EA4B8BA09604D62A62B /* AnswerCard.swift */; };
50 + 72A88C81BE80344130E0EC0C /* GuideAnswerService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3719031080B59898133810F7 /* GuideAnswerService.swift */; };
51 + 7C90532B70C1C4840E49A92B /* GuideDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7541983FF7CE190C0663294D /* GuideDetailView.swift */; };
52 7F1E744AAE60BF1FB132D66C /* KleponCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 868C58098969278A542893F3 /* KleponCard.swift */; };
53 8401F9C0FA140DC2CF8BA248 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF291E9C2E66209377138E6 /* AppState.swift */; };
54 8804CFBAD707E1FED63BF214 /* SearchService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6235AD6792F25476E2A508F0 /* SearchService.swift */; };
55 88984B6A8AA504AD200A7F39 /* collections.json in Resources */ = {isa = PBXBuildFile; fileRef = 1FDDB0F88FF430ABD679AD89 /* collections.json */; };
56 + 889CBF2AF84AABF8012EE737 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AF79D8DD27B9CA485140C934 /* Assets.xcassets */; };
57 + 895B5369E9CF1E1B26CDF40E /* KleponTypography.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD520C870387E82E9A1533C2 /* KleponTypography.swift */; };
58 + 8EBCC9FA074785FDD82B02E5 /* KleponApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEDE2A1E73EB15970A17A894 /* KleponApp.swift */; };
59 910A84B3F28B3F79871FF60E /* SectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC438ECC335175B18778C682 /* SectionHeader.swift */; };
60 926AB007EBCD5315EE77E2A6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AF79D8DD27B9CA485140C934 /* Assets.xcassets */; };
61 + 93E938AA978ABF5665737887 /* GuideEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0A0935AE4B398B281758834 /* GuideEntry.swift */; };
62 + 955D039F4B5D14FCA4E7F2B2 /* GuideArtworkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F2850226B4B05FF758C1F93 /* GuideArtworkView.swift */; };
63 99071E0CB9F44857C0BFC8C9 /* SavedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98AA2E425D5454602F78D628 /* SavedView.swift */; };
64 + 9E16F5C37F30B58441780260 /* DiscoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4545DC84863788D55B94D31A /* DiscoverView.swift */; };
65 9ECA989E9DCDF08A2D7C29A3 /* AppRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C82874C90A5FAA32D1B52EB7 /* AppRootView.swift */; };
66 + 9ECEB59572D3B9D08D1DA896 /* GuideCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = F17C0DB3BD620AD9F55B7631 /* GuideCollection.swift */; };
67 A54D028D8F35FE002B0ACA90 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 054656155630B064F785B0D7 /* Assets.xcassets */; };
68 A7001D58E0F2B1BD5F52FF8A /* Onde in Frameworks */ = {isa = PBXBuildFile; productRef = 855EE8DF62A7820E77C05D1A /* Onde */; };
69 B1F1D1A453150D3BBB0146EF /* RecentlyViewedStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E45070309D2C7058BAA44CD2 /* RecentlyViewedStore.swift */; };
70 B34377720179454F9B9184C5 /* OndeGuideEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0D53A5F9D5897E15506C20 /* OndeGuideEngine.swift */; };
71 + B4467AA7214B4990D2FC19DA /* KleponCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 868C58098969278A542893F3 /* KleponCard.swift */; };
72 + B768150B02B19417B81FE651 /* guide_entries.json in Resources */ = {isa = PBXBuildFile; fileRef = 248F359109A08A60F792C42D /* guide_entries.json */; };
73 B7DE9EC8BE998E6719C88E83 /* guide_entries.json in Resources */ = {isa = PBXBuildFile; fileRef = 248F359109A08A60F792C42D /* guide_entries.json */; };
74 + BB70FAD944544125AF261F7C /* SavedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98AA2E425D5454602F78D628 /* SavedView.swift */; };
75 + C15131992039B3AC7DB74DAB /* testGuideArtworkView.guide-artwork.png in Resources */ = {isa = PBXBuildFile; fileRef = 9D6EFA021D4524508DA8E78C /* testGuideArtworkView.guide-artwork.png */; };
76 C2EA2682A6545802C06E00CC /* WatchBrandSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217E7D4050D471955FF3AB13 /* WatchBrandSystem.swift */; };
77 C709C82E7F0BCF398BCD9C2F /* ContentRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2891A1AFBB59928FC5B1E66 /* ContentRepository.swift */; };
78 + D6D8A42930EA9A0EA6622108 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF291E9C2E66209377138E6 /* AppState.swift */; };
79 D99AAE1CDE51139840A52607 /* KleponMetadataRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7E1C50BFBE2EF5F37941590 /* KleponMetadataRow.swift */; };
80 DA915BA8B2F732E7D62CF936 /* EmptyStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E7B5E3D9A1906C2FED3032 /* EmptyStateView.swift */; };
81 E45F257E3D35DC565B21B299 /* KleponColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C675ABBBA25BBA25A8DE29BE /* KleponColor.swift */; };
82 E599CCDB1FE5331C4A1EB6C6 /* KleponSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 759E92BBAF958F5882F17C37 /* KleponSnapshotTests.swift */; };
83 + E87F8A08D6922557A5DBD198 /* testOnboardingView_defaultState.default.png in Resources */ = {isa = PBXBuildFile; fileRef = 414FD1A8945B820DCC4ED192 /* testOnboardingView_defaultState.default.png */; };
84 + ECDD606D8AFA776765AA9A31 /* SectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC438ECC335175B18778C682 /* SectionHeader.swift */; };
85 + EE2103530984735318E469FD /* AskSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC6E7DC56DD8E1459EFC892 /* AskSheetView.swift */; };
86 EE88AEC7059BB2639687FECF /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 35214C3FF433B08C1C06C3F7 /* SnapshotTesting */; };
87 + F0694A813D1CB57F0B5C0F65 /* FavoritesStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BC0C4C22A5134DB058E41F4 /* FavoritesStore.swift */; };
88 F1431179B54CEB23FFFCB2AE /* KleponApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEDE2A1E73EB15970A17A894 /* KleponApp.swift */; };
89 + F2D905B433B0B76CE6B4001F /* OndeEnvironmentBootstrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B499D3BF7AC80D31907D87F /* OndeEnvironmentBootstrap.swift */; };
90 + F46EB4EDFFF125ED88506D89 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 6B67BCC5A459288FB240C6E7 /* Info.plist */; };
91 + F5B35C6C771073CE32B4BFD1 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42E152344B39D1D77F1B3943 /* SettingsView.swift */; };
92 + F87EF3C291B7D3FB9BE8E43A /* ContentRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2891A1AFBB59928FC5B1E66 /* ContentRepository.swift */; };
93 + F88972525510344E63D2983C /* AppRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C82874C90A5FAA32D1B52EB7 /* AppRootView.swift */; };
94 + F9D6FC75A11BE98BD28BBDC2 /* RecentSearchStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 643D52BCEDF66CC8FE8A1234 /* RecentSearchStore.swift */; };
95 FA423A76BE098A9911044246 /* AskSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC6E7DC56DD8E1459EFC892 /* AskSheetView.swift */; };
96 FD0E1BA888E3CE73875D58FF /* RecentSearchStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 643D52BCEDF66CC8FE8A1234 /* RecentSearchStore.swift */; };
97 /* End PBXBuildFile section */
@@ -96,6 +137,7 @@
137 248F359109A08A60F792C42D /* guide_entries.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = guide_entries.json; sourceTree = "<group>"; };
138 2B499D3BF7AC80D31907D87F /* OndeEnvironmentBootstrap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OndeEnvironmentBootstrap.swift; sourceTree = "<group>"; };
139 3719031080B59898133810F7 /* GuideAnswerService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuideAnswerService.swift; sourceTree = "<group>"; };
140 + 414FD1A8945B820DCC4ED192 /* testOnboardingView_defaultState.default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = testOnboardingView_defaultState.default.png; sourceTree = "<group>"; };
141 42E152344B39D1D77F1B3943 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
142 4545DC84863788D55B94D31A /* DiscoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscoverView.swift; sourceTree = "<group>"; };
143 4C77AF5EB4B17D087FA522F6 /* WatchContentLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchContentLoader.swift; sourceTree = "<group>"; };
@@ -112,7 +154,10 @@
154 7F2850226B4B05FF758C1F93 /* GuideArtworkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuideArtworkView.swift; sourceTree = "<group>"; };
155 85E7B5E3D9A1906C2FED3032 /* EmptyStateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyStateView.swift; sourceTree = "<group>"; };
156 868C58098969278A542893F3 /* KleponCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KleponCard.swift; sourceTree = "<group>"; };
157 + 8870F09A11EA6D3F0108DA4D /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
158 + 943670F01925DE3065B92636 /* testEmptyStateView.saved-empty-state.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "testEmptyStateView.saved-empty-state.png"; sourceTree = "<group>"; };
159 98AA2E425D5454602F78D628 /* SavedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavedView.swift; sourceTree = "<group>"; };
160 + 9D6EFA021D4524508DA8E78C /* testGuideArtworkView.guide-artwork.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "testGuideArtworkView.guide-artwork.png"; sourceTree = "<group>"; };
161 9E76510CC840CE068B0FC381 /* Klepon Watch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Klepon Watch.app"; sourceTree = BUILT_PRODUCTS_DIR; };
162 A4400AB0AB8D245325A00BA3 /* Klepon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Klepon.entitlements; sourceTree = "<group>"; };
163 A48AC0E39277DEFCE5D103CC /* KleponSnapshotTests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = KleponSnapshotTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -125,6 +170,7 @@
170 C675ABBBA25BBA25A8DE29BE /* KleponColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KleponColor.swift; sourceTree = "<group>"; };
171 C82874C90A5FAA32D1B52EB7 /* AppRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRootView.swift; sourceTree = "<group>"; };
172 CD520C870387E82E9A1533C2 /* KleponTypography.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KleponTypography.swift; sourceTree = "<group>"; };
173 + D53EA049112C7D59177F9878 /* Klepon Mac.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Klepon Mac.app"; sourceTree = BUILT_PRODUCTS_DIR; };
174 DEDE2A1E73EB15970A17A894 /* KleponApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KleponApp.swift; sourceTree = "<group>"; };
175 E45070309D2C7058BAA44CD2 /* RecentlyViewedStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentlyViewedStore.swift; sourceTree = "<group>"; };
176 E7E886BAB66E828950DBED52 /* SearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchView.swift; sourceTree = "<group>"; };
@@ -132,10 +178,20 @@
178 EEC6E7DC56DD8E1459EFC892 /* AskSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AskSheetView.swift; sourceTree = "<group>"; };
179 F17C0DB3BD620AD9F55B7631 /* GuideCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuideCollection.swift; sourceTree = "<group>"; };
180 F2891A1AFBB59928FC5B1E66 /* ContentRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentRepository.swift; sourceTree = "<group>"; };
181 + F51D6F97615398D511673B98 /* PlatformViewSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformViewSupport.swift; sourceTree = "<group>"; };
182 FC438ECC335175B18778C682 /* SectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionHeader.swift; sourceTree = "<group>"; };
183 /* End PBXFileReference section */
184
185 /* Begin PBXFrameworksBuildPhase section */
186 + B0052635A882F0E996B81154 /* Frameworks */ = {
187 + isa = PBXFrameworksBuildPhase;
188 + buildActionMask = 2147483647;
189 + files = (
190 + 47B632AF6BF3E25AF628CC01 /* Onde in Frameworks */,
191 + 215C4D8A2B2ABF2C3C11C163 /* SystemConfiguration.framework in Frameworks */,
192 + );
193 + runOnlyForDeploymentPostprocessing = 0;
194 + };
195 B161A429683CBAD2A984A5AE /* Frameworks */ = {
196 isa = PBXFrameworksBuildPhase;
197 buildActionMask = 2147483647;
@@ -171,9 +227,18 @@
227 path = Settings;
228 sourceTree = "<group>";
229 };
230 + 2D223E83C424037A62CA358F /* __Snapshots__ */ = {
231 + isa = PBXGroup;
232 + children = (
233 + 89DF9DC044BB33376803C6F5 /* KleponSnapshotTests */,
234 + );
235 + path = __Snapshots__;
236 + sourceTree = "<group>";
237 + };
238 3F9369D339C14126AB44D720 /* Products */ = {
239 isa = PBXGroup;
240 children = (
241 + D53EA049112C7D59177F9878 /* Klepon Mac.app */,
242 9E76510CC840CE068B0FC381 /* Klepon Watch.app */,
243 5DE91827A635C0B0B72690EC /* Klepon.app */,
244 A48AC0E39277DEFCE5D103CC /* KleponSnapshotTests.xctest */,
@@ -246,6 +311,7 @@
311 C82874C90A5FAA32D1B52EB7 /* AppRootView.swift */,
312 5AF291E9C2E66209377138E6 /* AppState.swift */,
313 DEDE2A1E73EB15970A17A894 /* KleponApp.swift */,
314 + F51D6F97615398D511673B98 /* PlatformViewSupport.swift */,
315 );
316 path = App;
317 sourceTree = "<group>";
@@ -254,6 +320,7 @@
320 isa = PBXGroup;
321 children = (
322 759E92BBAF958F5882F17C37 /* KleponSnapshotTests.swift */,
323 + 2D223E83C424037A62CA358F /* __Snapshots__ */,
324 );
325 path = KleponSnapshotTests;
326 sourceTree = "<group>";
@@ -286,6 +353,16 @@
353 path = Domain;
354 sourceTree = "<group>";
355 };
356 + 89DF9DC044BB33376803C6F5 /* KleponSnapshotTests */ = {
357 + isa = PBXGroup;
358 + children = (
359 + 943670F01925DE3065B92636 /* testEmptyStateView.saved-empty-state.png */,
360 + 9D6EFA021D4524508DA8E78C /* testGuideArtworkView.guide-artwork.png */,
361 + 414FD1A8945B820DCC4ED192 /* testOnboardingView_defaultState.default.png */,
362 + );
363 + path = KleponSnapshotTests;
364 + sourceTree = "<group>";
365 + };
366 8FCB4D289A8BA31F1213435D /* Resources */ = {
367 isa = PBXGroup;
368 children = (
@@ -346,6 +423,7 @@
423 EA4DFA44FE8BB01979ADB187 /* Klepon */,
424 825A58FCE091C3FE902D3131 /* KleponSnapshotTests */,
425 472BF5D47BEE4AF1EC80B645 /* KleponWatch */,
426 + EC85AFC077F4445CA6E5B876 /* Frameworks */,
427 3F9369D339C14126AB44D720 /* Products */,
428 );
429 sourceTree = "<group>";
@@ -367,6 +445,14 @@
445 path = Klepon;
446 sourceTree = "<group>";
447 };
448 + EC85AFC077F4445CA6E5B876 /* Frameworks */ = {
449 + isa = PBXGroup;
450 + children = (
451 + 8870F09A11EA6D3F0108DA4D /* SystemConfiguration.framework */,
452 + );
453 + name = Frameworks;
454 + sourceTree = "<group>";
455 + };
456 /* End PBXGroup section */
457
458 /* Begin PBXNativeTarget section */
@@ -393,6 +479,27 @@
479 productReference = 5DE91827A635C0B0B72690EC /* Klepon.app */;
480 productType = "com.apple.product-type.application";
481 };
482 + 676CA86C78E7DB801DEEAF12 /* Klepon Mac */ = {
483 + isa = PBXNativeTarget;
484 + buildConfigurationList = 78899F7C62A9675700D42C5A /* Build configuration list for PBXNativeTarget "Klepon Mac" */;
485 + buildPhases = (
486 + 625CD4834F7E62F4BCE58404 /* Sources */,
487 + 667705EEBEDEBBCA43E94EAC /* Resources */,
488 + B0052635A882F0E996B81154 /* Frameworks */,
489 + 38259E5F7F0F32D684AE509A /* Update CFBundleVersion */,
490 + );
491 + buildRules = (
492 + );
493 + dependencies = (
494 + );
495 + name = "Klepon Mac";
496 + packageProductDependencies = (
497 + 35BDA28E3F43D5441C5FE932 /* Onde */,
498 + );
499 + productName = "Klepon Mac";
500 + productReference = D53EA049112C7D59177F9878 /* Klepon Mac.app */;
501 + productType = "com.apple.product-type.application";
502 + };
503 E8CEBF52617721AEE450A7CF /* Klepon Watch */ = {
504 isa = PBXNativeTarget;
505 buildConfigurationList = 07A758477E97CF9254DDBA82 /* Build configuration list for PBXNativeTarget "Klepon Watch" */;
@@ -417,6 +524,7 @@
524 buildConfigurationList = 116221FC6D50F33334387509 /* Build configuration list for PBXNativeTarget "KleponSnapshotTests" */;
525 buildPhases = (
526 C2B1B8ADEE7B8E2628B90D1F /* Sources */,
527 + 6F8A4C98AF649CB41F0837B3 /* Resources */,
528 B161A429683CBAD2A984A5AE /* Frameworks */,
529 );
530 buildRules = (
@@ -444,6 +552,9 @@
552 2ADB11F6C1D51B9F9A9DB15F = {
553 ProvisioningStyle = Automatic;
554 };
555 + 676CA86C78E7DB801DEEAF12 = {
556 + ProvisioningStyle = Automatic;
557 + };
558 E8CEBF52617721AEE450A7CF = {
559 ProvisioningStyle = Automatic;
560 };
@@ -471,6 +582,7 @@
582 projectRoot = "";
583 targets = (
584 2ADB11F6C1D51B9F9A9DB15F /* Klepon */,
585 + 676CA86C78E7DB801DEEAF12 /* Klepon Mac */,
586 E8CEBF52617721AEE450A7CF /* Klepon Watch */,
587 F10E9562BA0BE60A7E000DB0 /* KleponSnapshotTests */,
588 );
@@ -488,6 +600,27 @@
600 );
601 runOnlyForDeploymentPostprocessing = 0;
602 };
603 + 667705EEBEDEBBCA43E94EAC /* Resources */ = {
604 + isa = PBXResourcesBuildPhase;
605 + buildActionMask = 2147483647;
606 + files = (
607 + 889CBF2AF84AABF8012EE737 /* Assets.xcassets in Resources */,
608 + F46EB4EDFFF125ED88506D89 /* Info.plist in Resources */,
609 + 5692BBCE950885C5C1193DDF /* collections.json in Resources */,
610 + B768150B02B19417B81FE651 /* guide_entries.json in Resources */,
611 + );
612 + runOnlyForDeploymentPostprocessing = 0;
613 + };
614 + 6F8A4C98AF649CB41F0837B3 /* Resources */ = {
615 + isa = PBXResourcesBuildPhase;
616 + buildActionMask = 2147483647;
617 + files = (
618 + 35D43509610D0E2CB3DBD2BB /* testEmptyStateView.saved-empty-state.png in Resources */,
619 + C15131992039B3AC7DB74DAB /* testGuideArtworkView.guide-artwork.png in Resources */,
620 + E87F8A08D6922557A5DBD198 /* testOnboardingView_defaultState.default.png in Resources */,
621 + );
622 + runOnlyForDeploymentPostprocessing = 0;
623 + };
624 BC5A6BBAFB4A1B61000065D1 /* Resources */ = {
625 isa = PBXResourcesBuildPhase;
626 buildActionMask = 2147483647;
@@ -521,6 +654,26 @@
654 shellScript = "set -eu\n\nSHARED_BUNDLE_VERSION_FILE=\"${BUILD_DIR}/klepon-cfbundleversion.txt\"\nif [ -f \"${SHARED_BUNDLE_VERSION_FILE}\" ]; then\n BUNDLE_VERSION=\"$(cat \"${SHARED_BUNDLE_VERSION_FILE}\")\"\nelse\n BUNDLE_VERSION=\"$(date -u +\"%Y%m%d.%H%M\")\"\n printf '%s' \"${BUNDLE_VERSION}\" > \"${SHARED_BUNDLE_VERSION_FILE}\"\nfi\n\nINFO_PLIST=\"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\n\nif [ ! -f \"${INFO_PLIST}\" ]; then\n echo \"error: Info.plist not found at ${INFO_PLIST}\"\n exit 1\nfi\n\nif ! /usr/libexec/PlistBuddy -c \"Set :CFBundleVersion ${BUNDLE_VERSION}\" \"${INFO_PLIST}\" >/dev/null 2>&1; then\n /usr/libexec/PlistBuddy -c \"Add :CFBundleVersion string ${BUNDLE_VERSION}\" \"${INFO_PLIST}\"\nfi\n\necho \"Updated CFBundleVersion to ${BUNDLE_VERSION}\"\n";
655 showEnvVarsInLog = 0;
656 };
657 + 38259E5F7F0F32D684AE509A /* Update CFBundleVersion */ = {
658 + isa = PBXShellScriptBuildPhase;
659 + alwaysOutOfDate = 1;
660 + buildActionMask = 2147483647;
661 + files = (
662 + );
663 + inputFileListPaths = (
664 + );
665 + inputPaths = (
666 + );
667 + name = "Update CFBundleVersion";
668 + outputFileListPaths = (
669 + );
670 + outputPaths = (
671 + );
672 + runOnlyForDeploymentPostprocessing = 0;
673 + shellPath = /bin/sh;
674 + shellScript = "set -eu\n\nSHARED_BUNDLE_VERSION_FILE=\"${BUILD_DIR}/klepon-cfbundleversion.txt\"\nif [ -f \"${SHARED_BUNDLE_VERSION_FILE}\" ]; then\n BUNDLE_VERSION=\"$(cat \"${SHARED_BUNDLE_VERSION_FILE}\")\"\nelse\n BUNDLE_VERSION=\"$(date -u +\"%Y%m%d.%H%M\")\"\n printf '%s' \"${BUNDLE_VERSION}\" > \"${SHARED_BUNDLE_VERSION_FILE}\"\nfi\n\nINFO_PLIST=\"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\n\nif [ ! -f \"${INFO_PLIST}\" ]; then\n echo \"error: Info.plist not found at ${INFO_PLIST}\"\n exit 1\nfi\n\nif ! /usr/libexec/PlistBuddy -c \"Set :CFBundleVersion ${BUNDLE_VERSION}\" \"${INFO_PLIST}\" >/dev/null 2>&1; then\n /usr/libexec/PlistBuddy -c \"Add :CFBundleVersion string ${BUNDLE_VERSION}\" \"${INFO_PLIST}\"\nfi\n\necho \"Updated CFBundleVersion to ${BUNDLE_VERSION}\"\n";
675 + showEnvVarsInLog = 0;
676 + };
677 4CD2E0A71BF112F74656BFFD /* Update CFBundleVersion */ = {
678 isa = PBXShellScriptBuildPhase;
679 alwaysOutOfDate = 1;
@@ -571,6 +724,7 @@
724 41F8E173B82809AF87E1B297 /* OnboardingView.swift in Sources */,
725 67368394088AA3045EBA14A5 /* OndeEnvironmentBootstrap.swift in Sources */,
726 B34377720179454F9B9184C5 /* OndeGuideEngine.swift in Sources */,
727 + 37A723A15D19B7DE3DD92B25 /* PlatformViewSupport.swift in Sources */,
728 FD0E1BA888E3CE73875D58FF /* RecentSearchStore.swift in Sources */,
729 B1F1D1A453150D3BBB0146EF /* RecentlyViewedStore.swift in Sources */,
730 99071E0CB9F44857C0BFC8C9 /* SavedView.swift in Sources */,
@@ -594,6 +748,44 @@
748 );
749 runOnlyForDeploymentPostprocessing = 0;
750 };
751 + 625CD4834F7E62F4BCE58404 /* Sources */ = {
752 + isa = PBXSourcesBuildPhase;
753 + buildActionMask = 2147483647;
754 + files = (
755 + 687418866BCC5554A0F4BFDC /* AnswerCard.swift in Sources */,
756 + F88972525510344E63D2983C /* AppRootView.swift in Sources */,
757 + D6D8A42930EA9A0EA6622108 /* AppState.swift in Sources */,
758 + EE2103530984735318E469FD /* AskSheetView.swift in Sources */,
759 + F87EF3C291B7D3FB9BE8E43A /* ContentRepository.swift in Sources */,
760 + 9E16F5C37F30B58441780260 /* DiscoverView.swift in Sources */,
761 + 2AD05B0B7ED109134F013862 /* EmptyStateView.swift in Sources */,
762 + F0694A813D1CB57F0B5C0F65 /* FavoritesStore.swift in Sources */,
763 + 72A88C81BE80344130E0EC0C /* GuideAnswerService.swift in Sources */,
764 + 955D039F4B5D14FCA4E7F2B2 /* GuideArtworkView.swift in Sources */,
765 + 9ECEB59572D3B9D08D1DA896 /* GuideCollection.swift in Sources */,
766 + 7C90532B70C1C4840E49A92B /* GuideDetailView.swift in Sources */,
767 + 93E938AA978ABF5665737887 /* GuideEntry.swift in Sources */,
768 + 5808C4539EBB98D822BDE99F /* KleponActionButton.swift in Sources */,
769 + 8EBCC9FA074785FDD82B02E5 /* KleponApp.swift in Sources */,
770 + B4467AA7214B4990D2FC19DA /* KleponCard.swift in Sources */,
771 + 1879460ECC35F3386E8E6109 /* KleponChip.swift in Sources */,
772 + 3F999E04650931C2019BDA17 /* KleponColor.swift in Sources */,
773 + 43ABAA3C08695B7C8FEE7324 /* KleponMetadataRow.swift in Sources */,
774 + 895B5369E9CF1E1B26CDF40E /* KleponTypography.swift in Sources */,
775 + 2F3047F5078C4526133CB848 /* OnboardingView.swift in Sources */,
776 + F2D905B433B0B76CE6B4001F /* OndeEnvironmentBootstrap.swift in Sources */,
777 + 37BD5B2A275426143819B70D /* OndeGuideEngine.swift in Sources */,
778 + 35B5508906432B3EFE26F4FF /* PlatformViewSupport.swift in Sources */,
779 + F9D6FC75A11BE98BD28BBDC2 /* RecentSearchStore.swift in Sources */,
780 + 3A6F67743964C4D04893C321 /* RecentlyViewedStore.swift in Sources */,
781 + BB70FAD944544125AF261F7C /* SavedView.swift in Sources */,
782 + 0709F0B92923D97F6859275A /* SearchService.swift in Sources */,
783 + 032AFB30CAB4E8A31CDC897D /* SearchView.swift in Sources */,
784 + ECDD606D8AFA776765AA9A31 /* SectionHeader.swift in Sources */,
785 + F5B35C6C771073CE32B4BFD1 /* SettingsView.swift in Sources */,
786 + );
787 + runOnlyForDeploymentPostprocessing = 0;
788 + };
789 C2B1B8ADEE7B8E2628B90D1F /* Sources */ = {
790 isa = PBXSourcesBuildPhase;
791 buildActionMask = 2147483647;
@@ -684,6 +876,56 @@
876 };
877 name = Debug;
878 };
879 + 73320483BA240362017C25C8 /* Debug */ = {
880 + isa = XCBuildConfiguration;
881 + baseConfigurationReference = 13B09D700AA047E07E91F6A2 /* Signing.xcconfig */;
882 + buildSettings = {
883 + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
884 + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
885 + CODE_SIGN_ENTITLEMENTS = "";
886 + COMBINE_HIDPI_IMAGES = YES;
887 + CURRENT_PROJECT_VERSION = 1;
888 + ENABLE_USER_SCRIPT_SANDBOXING = NO;
889 + "EXCLUDED_ARCHS[sdk=macosx*]" = x86_64;
890 + INFOPLIST_FILE = KleponMac/Info.plist;
891 + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2026 Splitfire AB";
892 + LD_RUNPATH_SEARCH_PATHS = (
893 + "$(inherited)",
894 + "@executable_path/../Frameworks",
895 + );
896 + MARKETING_VERSION = 1.0.0;
897 + PRODUCT_BUNDLE_IDENTIFIER = ai.splitfire.klepon.mac;
898 + PRODUCT_NAME = Klepon;
899 + SDKROOT = macosx;
900 + SWIFT_OPTIMIZATION_LEVEL = "-Onone";
901 + };
902 + name = Debug;
903 + };
904 + 812F1D8D209CC3C18A9DC330 /* Release */ = {
905 + isa = XCBuildConfiguration;
906 + baseConfigurationReference = 13B09D700AA047E07E91F6A2 /* Signing.xcconfig */;
907 + buildSettings = {
908 + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
909 + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
910 + CODE_SIGN_ENTITLEMENTS = "";
911 + COMBINE_HIDPI_IMAGES = YES;
912 + CURRENT_PROJECT_VERSION = 1;
913 + ENABLE_USER_SCRIPT_SANDBOXING = NO;
914 + "EXCLUDED_ARCHS[sdk=macosx*]" = x86_64;
915 + INFOPLIST_FILE = KleponMac/Info.plist;
916 + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2026 Splitfire AB";
917 + LD_RUNPATH_SEARCH_PATHS = (
918 + "$(inherited)",
919 + "@executable_path/../Frameworks",
920 + );
921 + MARKETING_VERSION = 1.0.0;
922 + PRODUCT_BUNDLE_IDENTIFIER = ai.splitfire.klepon.mac;
923 + PRODUCT_NAME = Klepon;
924 + SDKROOT = macosx;
925 + SWIFT_OPTIMIZATION_LEVEL = "-O";
926 + };
927 + name = Release;
928 + };
929 850753164F664131E0E37EAE /* Release */ = {
930 isa = XCBuildConfiguration;
931 buildSettings = {
@@ -730,6 +972,7 @@
972 GCC_WARN_UNUSED_FUNCTION = YES;
973 GCC_WARN_UNUSED_VARIABLE = YES;
974 IPHONEOS_DEPLOYMENT_TARGET = 18.5;
975 + MACOSX_DEPLOYMENT_TARGET = 15.0;
976 MTL_ENABLE_DEBUG_INFO = NO;
977 MTL_FAST_MATH = YES;
978 PRODUCT_NAME = "$(TARGET_NAME)";
@@ -842,6 +1085,7 @@
1085 GCC_WARN_UNUSED_FUNCTION = YES;
1086 GCC_WARN_UNUSED_VARIABLE = YES;
1087 IPHONEOS_DEPLOYMENT_TARGET = 18.5;
1088 + MACOSX_DEPLOYMENT_TARGET = 15.0;
1089 MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
1090 MTL_FAST_MATH = YES;
1091 ONLY_ACTIVE_ARCH = YES;
@@ -898,6 +1142,15 @@
1142 defaultConfigurationIsVisible = 0;
1143 defaultConfigurationName = Debug;
1144 };
1145 + 78899F7C62A9675700D42C5A /* Build configuration list for PBXNativeTarget "Klepon Mac" */ = {
1146 + isa = XCConfigurationList;
1147 + buildConfigurations = (
1148 + 73320483BA240362017C25C8 /* Debug */,
1149 + 812F1D8D209CC3C18A9DC330 /* Release */,
1150 + );
1151 + defaultConfigurationIsVisible = 0;
1152 + defaultConfigurationName = Debug;
1153 + };
1154 8911E6C6F25C3A4F99870749 /* Build configuration list for PBXProject "Klepon" */ = {
1155 isa = XCConfigurationList;
1156 buildConfigurations = (
@@ -943,6 +1196,11 @@
1196 package = 85B622113A5DC5A1BD2D123B /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */;
1197 productName = SnapshotTesting;
1198 };
1199 + 35BDA28E3F43D5441C5FE932 /* Onde */ = {
1200 + isa = XCSwiftPackageProductDependency;
1201 + package = AD8C734FB461604D5793769C /* XCRemoteSwiftPackageReference "onde-swift" */;
1202 + productName = Onde;
1203 + };
1204 855EE8DF62A7820E77C05D1A /* Onde */ = {
1205 isa = XCSwiftPackageProductDependency;
1206 package = AD8C734FB461604D5793769C /* XCRemoteSwiftPackageReference "onde-swift" */;
Klepon.xcodeproj/xcshareddata/xcschemes/Klepon Mac.xcscheme new
+87
@@ -0,0 +1,87 @@
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<Scheme
3 + LastUpgradeVersion = "2600"
4 + version = "1.3">
5 + <BuildAction
6 + parallelizeBuildables = "YES"
7 + buildImplicitDependencies = "YES">
8 + <BuildActionEntries>
9 + <BuildActionEntry
10 + buildForTesting = "YES"
11 + buildForRunning = "YES"
12 + buildForProfiling = "YES"
13 + buildForArchiving = "YES"
14 + buildForAnalyzing = "YES">
15 + <BuildableReference
16 + BuildableIdentifier = "primary"
17 + BlueprintIdentifier = "676CA86C78E7DB801DEEAF12"
18 + BuildableName = "Klepon.app"
19 + BlueprintName = "Klepon Mac"
20 + ReferencedContainer = "container:Klepon.xcodeproj">
21 + </BuildableReference>
22 + </BuildActionEntry>
23 + </BuildActionEntries>
24 + </BuildAction>
25 + <TestAction
26 + buildConfiguration = "Debug"
27 + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
28 + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29 + shouldUseLaunchSchemeArgsEnv = "YES">
30 + <MacroExpansion>
31 + <BuildableReference
32 + BuildableIdentifier = "primary"
33 + BlueprintIdentifier = "676CA86C78E7DB801DEEAF12"
34 + BuildableName = "Klepon.app"
35 + BlueprintName = "Klepon Mac"
36 + ReferencedContainer = "container:Klepon.xcodeproj">
37 + </BuildableReference>
38 + </MacroExpansion>
39 + <Testables>
40 + </Testables>
41 + </TestAction>
42 + <LaunchAction
43 + buildConfiguration = "Debug"
44 + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
45 + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
46 + launchStyle = "0"
47 + useCustomWorkingDirectory = "NO"
48 + ignoresPersistentStateOnLaunch = "NO"
49 + debugDocumentVersioning = "YES"
50 + debugServiceExtension = "internal"
51 + allowLocationSimulation = "YES">
52 + <BuildableProductRunnable
53 + runnableDebuggingMode = "0">
54 + <BuildableReference
55 + BuildableIdentifier = "primary"
56 + BlueprintIdentifier = "676CA86C78E7DB801DEEAF12"
57 + BuildableName = "Klepon.app"
58 + BlueprintName = "Klepon Mac"
59 + ReferencedContainer = "container:Klepon.xcodeproj">
60 + </BuildableReference>
61 + </BuildableProductRunnable>
62 + </LaunchAction>
63 + <ProfileAction
64 + buildConfiguration = "Release"
65 + shouldUseLaunchSchemeArgsEnv = "YES"
66 + savedToolIdentifier = ""
67 + useCustomWorkingDirectory = "NO"
68 + debugDocumentVersioning = "YES">
69 + <BuildableProductRunnable
70 + runnableDebuggingMode = "0">
71 + <BuildableReference
72 + BuildableIdentifier = "primary"
73 + BlueprintIdentifier = "676CA86C78E7DB801DEEAF12"
74 + BuildableName = "Klepon.app"
75 + BlueprintName = "Klepon Mac"
76 + ReferencedContainer = "container:Klepon.xcodeproj">
77 + </BuildableReference>
78 + </BuildableProductRunnable>
79 + </ProfileAction>
80 + <AnalyzeAction
81 + buildConfiguration = "Debug">
82 + </AnalyzeAction>
83 + <ArchiveAction
84 + buildConfiguration = "Release"
85 + revealArchiveInOrganizer = "YES">
86 + </ArchiveAction>
87 +</Scheme>
Klepon/AI/GuideAnswerService.swift
+1 -1
@@ -57,7 +57,7 @@ final class GuideAnswerService {
57 let previousContext = previousConversationContext(from: previousCards)
58
59 return """
60 - You are answering inside Klepon, a private iPhone guide to Indonesian food.
60 + You are answering inside Klepon, a private local guide to Indonesian food.
61 Use only the guide notes below.
62 If the notes are not enough, say the guide does not have enough detail yet.
63 Keep the tone warm, respectful, and concise.
Klepon/AI/OndeGuideEngine.swift
+4 -4
@@ -15,11 +15,11 @@ enum PrivateGuideAvailability: Equatable {
15 case .notInstalled:
16 return "Private guide not ready yet"
17 case .downloaded:
18 - return "Private guide downloaded on this iPhone"
18 + return "Private guide downloaded on this device"
19 case .preparing:
20 return "Preparing your private guide"
21 case .ready:
22 - return "Private guide ready on this iPhone"
22 + return "Private guide ready on this device"
23 case .answering:
24 return "Answering privately"
25 case .failed:
@@ -34,10 +34,10 @@ enum PrivateGuideAvailability: Equatable {
34 "You can browse everything first, then add a one-time private guide download whenever you want deeper follow-up answers."
35 case .downloaded:
36 return
37 - "The private guide is already stored on this iPhone. Finish preparing it when you want faster follow-up answers in the app."
37 + "The private guide is already stored on this device. Finish preparing it when you want faster follow-up answers in the app."
38 case .preparing:
39 return
40 - "The first run can take a while because Klepon needs to download and load the private guide locally on your iPhone."
40 + "The first run can take a while because Klepon needs to download and load the private guide locally on your device."
41 case .ready:
42 return
43 "Klepon can now answer follow-up questions privately without turning the app into a generic chat shell."
Klepon/App/AppRootView.swift
+10 -1
@@ -45,8 +45,11 @@ struct AppRootView: View {
45 NavigationStack {
46 SettingsView()
47 }
48 + #if os(macOS)
49 + .frame(minWidth: 480, minHeight: 620)
50 + #endif
51 }
49 - .fullScreenCover(isPresented: $showingOnboarding) {
52 + .kleponOnboardingPresentation(isPresented: $showingOnboarding) {
53 OnboardingView(
54 onBrowseFirst: {
55 appState.completeOnboarding()
@@ -55,6 +58,9 @@ struct AppRootView: View {
58 appState.completeOnboarding()
59 }
60 )
61 + #if os(macOS)
62 + .frame(minWidth: 640, minHeight: 760)
63 + #endif
64 }
65 .task {
66 showingOnboarding = !appState.hasCompletedOnboarding
@@ -63,5 +69,8 @@ struct AppRootView: View {
69 .onChange(of: appState.hasCompletedOnboarding) { _, newValue in
70 showingOnboarding = !newValue
71 }
72 + #if os(macOS)
73 + .frame(minWidth: 980, minHeight: 720)
74 + #endif
75 }
76 }
Klepon/App/KleponApp.swift
+6 -1
@@ -12,7 +12,12 @@ struct KleponApp: App {
12 .environmentObject(appState.recentSearchStore)
13 .environmentObject(appState.recentlyViewedStore)
14 .environmentObject(appState.guideEngine)
15 - .preferredColorScheme(.light)
15 + #if os(iOS)
16 + .preferredColorScheme(.light)
17 + #endif
18 }
19 + #if os(macOS)
20 + .defaultSize(width: 1100, height: 760)
21 + #endif
22 }
23 }
Klepon/App/PlatformViewSupport.swift new
+43
@@ -0,0 +1,43 @@
1 +import SwiftUI
2 +
3 +extension ToolbarItemPlacement {
4 + static var kleponPrimaryAction: ToolbarItemPlacement {
5 + #if os(macOS)
6 + return .primaryAction
7 + #else
8 + return .topBarTrailing
9 + #endif
10 + }
11 +}
12 +
13 +extension View {
14 + @ViewBuilder
15 + func kleponInlineNavigationTitle() -> some View {
16 + #if os(iOS)
17 + self.navigationBarTitleDisplayMode(.inline)
18 + #else
19 + self
20 + #endif
21 + }
22 +
23 + @ViewBuilder
24 + func kleponLargeNavigationTitle() -> some View {
25 + #if os(iOS)
26 + self.navigationBarTitleDisplayMode(.large)
27 + #else
28 + self
29 + #endif
30 + }
31 +
32 + @ViewBuilder
33 + func kleponOnboardingPresentation<Content: View>(
34 + isPresented: Binding<Bool>,
35 + @ViewBuilder content: @escaping () -> Content
36 + ) -> some View {
37 + #if os(macOS)
38 + self.sheet(isPresented: isPresented, content: content)
39 + #else
40 + self.fullScreenCover(isPresented: isPresented, content: content)
41 + #endif
42 + }
43 +}
Klepon/Features/Ask/AskSheetView.swift
+3 -3
@@ -158,9 +158,9 @@ struct AskSheetView: View {
158 }
159 .background(KleponColor.background.ignoresSafeArea())
160 .navigationTitle("Ask privately")
161 - .navigationBarTitleDisplayMode(.inline)
161 + .kleponInlineNavigationTitle()
162 .toolbar {
163 - ToolbarItem(placement: .topBarTrailing) {
163 + ToolbarItem(placement: .kleponPrimaryAction) {
164 Button("Done") {
165 dismiss()
166 }
@@ -212,7 +212,7 @@ private struct AnswerCardView: View {
212 Spacer(minLength: 12)
213
214 if card.isGeneratedOnDevice {
215 - KleponChip(title: "On this iPhone", icon: "lock")
215 + KleponChip(title: "On this device", icon: "lock")
216 } else {
217 KleponChip(title: "Curated fallback", icon: "book")
218 }
Klepon/Features/Detail/GuideDetailView.swift
+2 -2
@@ -90,9 +90,9 @@ struct GuideDetailView: View {
90 }
91 .background(KleponColor.background.ignoresSafeArea())
92 .navigationTitle(entry.title)
93 - .navigationBarTitleDisplayMode(.inline)
93 + .kleponInlineNavigationTitle()
94 .toolbar {
95 - ToolbarItem(placement: .topBarTrailing) {
95 + ToolbarItem(placement: .kleponPrimaryAction) {
96 Button {
97 favoritesStore.toggle(entry.id)
98 } label: {
Klepon/Features/Discover/DiscoverView.swift
+4 -4
@@ -19,7 +19,7 @@ struct DiscoverView: View {
19 }
20
21 private var featuredTasteNotes: [String] {
22 - ["Private on your iPhone", "Curated first", "Warm follow-up answers"]
22 + ["Private on your device", "Curated first", "Warm follow-up answers"]
23 }
24
25 private var recentlyViewedEntries: [GuideEntry] {
@@ -104,9 +104,9 @@ struct DiscoverView: View {
104 }
105 .background(KleponColor.background.ignoresSafeArea())
106 .navigationTitle("Klepon")
107 - .navigationBarTitleDisplayMode(.large)
107 + .kleponLargeNavigationTitle()
108 .toolbar {
109 - ToolbarItem(placement: .topBarTrailing) {
109 + ToolbarItem(placement: .kleponPrimaryAction) {
110 Button {
111 showingSettings = true
112 } label: {
@@ -125,7 +125,7 @@ struct DiscoverView: View {
125 .foregroundStyle(KleponColor.textPrimary)
126
127 Text(
128 - "Explore dishes, ingredients, and food traditions with a guide that feels calm, personal, and private on your iPhone."
128 + "Explore dishes, ingredients, and food traditions with a guide that feels calm, personal, and private on your device."
129 )
130 .font(KleponTypography.body)
131 .foregroundStyle(KleponColor.textSecondary)
Klepon/Features/Onboarding/OnboardingView.swift
+35 -22
@@ -47,7 +47,7 @@ struct OnboardingView: View {
47
48 KleponCard {
49 onboardingPoint(
50 - title: "Private on your iPhone",
50 + title: "Private on your device",
51 detail:
52 "Private answers use a one-time on-device guide download. You can browse first and add it later.",
53 systemImage: "lock"
@@ -94,31 +94,44 @@ struct OnboardingView: View {
94 .padding(.bottom, 180)
95 }
96 .background(KleponColor.background.ignoresSafeArea())
97 - .safeAreaInset(edge: .bottom) {
98 - VStack(spacing: 12) {
99 - KleponActionButton(
100 - title: privateGuideButtonTitle,
101 - systemImage: "lock",
102 - isLoading: guideEngine.availability.isBusy,
103 - isDisabled: guideEngine.availability == .ready
104 - ) {
105 - Task {
106 - await guideEngine.prepareIfNeeded(
107 - forceReload: guideEngine.availability.isFailure)
108 - if guideEngine.availability == .ready {
109 - onComplete()
110 - }
97 + #if os(macOS)
98 + .safeAreaInset(edge: .bottom) {
99 + onboardingActions
100 + .padding(.horizontal, 24)
101 + .padding(.vertical, 16)
102 + .background(KleponColor.background)
103 + }
104 + #else
105 + .safeAreaInset(edge: .bottom) {
106 + onboardingActions
107 + .padding(.horizontal, 24)
108 + .padding(.top, 12)
109 + .padding(.bottom, 16)
110 + .background(.ultraThinMaterial)
111 + }
112 + #endif
113 + }
114 +
115 + private var onboardingActions: some View {
116 + VStack(spacing: 12) {
117 + KleponActionButton(
118 + title: privateGuideButtonTitle,
119 + systemImage: "lock",
120 + isLoading: guideEngine.availability.isBusy,
121 + isDisabled: guideEngine.availability == .ready
122 + ) {
123 + Task {
124 + await guideEngine.prepareIfNeeded(
125 + forceReload: guideEngine.availability.isFailure)
126 + if guideEngine.availability == .ready {
127 + onComplete()
128 }
129 }
130 + }
131
114 - KleponActionButton(title: "Browse first", tone: .secondary) {
115 - onBrowseFirst()
116 - }
132 + KleponActionButton(title: "Browse first", tone: .secondary) {
133 + onBrowseFirst()
134 }
118 - .padding(.horizontal, 24)
119 - .padding(.top, 12)
120 - .padding(.bottom, 16)
121 - .background(.ultraThinMaterial)
135 }
136 }
137
Klepon/Features/Settings/SettingsView.swift
+2 -2
@@ -57,7 +57,7 @@ struct SettingsView: View {
57
58 if guideEngine.storageUsedDescription != "Not downloaded yet" {
59 KleponActionButton(
60 - title: "Remove private guide from this iPhone",
60 + title: "Remove private guide from this device",
61 systemImage: "trash",
62 tone: .secondary
63 ) {
@@ -169,7 +169,7 @@ struct SettingsView: View {
169 .background(KleponColor.background.ignoresSafeArea())
170 .navigationTitle("Settings")
171 .toolbar {
172 - ToolbarItem(placement: .topBarTrailing) {
172 + ToolbarItem(placement: .kleponPrimaryAction) {
173 Button("Done") {
174 dismiss()
175 }
KleponMac/Info.plist new
+24
@@ -0,0 +1,24 @@
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3 +<plist version="1.0">
4 +<dict>
5 + <key>CFBundleDevelopmentRegion</key>
6 + <string>$(DEVELOPMENT_LANGUAGE)</string>
7 + <key>CFBundleExecutable</key>
8 + <string>$(EXECUTABLE_NAME)</string>
9 + <key>CFBundleIdentifier</key>
10 + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11 + <key>CFBundleInfoDictionaryVersion</key>
12 + <string>6.0</string>
13 + <key>CFBundleName</key>
14 + <string>$(PRODUCT_NAME)</string>
15 + <key>CFBundlePackageType</key>
16 + <string>APPL</string>
17 + <key>CFBundleShortVersionString</key>
18 + <string>$(MARKETING_VERSION)</string>
19 + <key>CFBundleVersion</key>
20 + <string>$(CURRENT_PROJECT_VERSION)</string>
21 + <key>LSApplicationCategoryType</key>
22 + <string>public.app-category.food-and-drink</string>
23 +</dict>
24 +</plist>
KleponSnapshotTests/KleponSnapshotTests.swift
+10
@@ -6,6 +6,16 @@ import XCTest
6
7 @MainActor
8 final class KleponSnapshotTests: XCTestCase {
9 + override func invokeTest() {
10 + if ProcessInfo.processInfo.environment["RECORD_SNAPSHOTS"] == "1" {
11 + withSnapshotTesting(record: .all) {
12 + super.invokeTest()
13 + }
14 + } else {
15 + super.invokeTest()
16 + }
17 + }
18 +
19 override func setUp() {
20 super.setUp()
21 continueAfterFailure = false
KleponSnapshotTests/__Snapshots__/KleponSnapshotTests/testOnboardingView_defaultState.default.png
Binary files a/KleponSnapshotTests/__Snapshots__/KleponSnapshotTests/testOnboardingView_defaultState.default.png and b/KleponSnapshotTests/__Snapshots__/KleponSnapshotTests/testOnboardingView_defaultState.default.png differ
project.yml
+77
@@ -2,6 +2,7 @@ name: Klepon
2 options:
3 deploymentTarget:
4 iOS: "18.5"
5 + macOS: "15.0"
6 watchOS: "9.0"
7 xcodeVersion: "26.0"
8 generateEmptyDirectories: false
@@ -48,6 +49,18 @@ schemes:
49 config: Release
50 profile:
51 config: Release
52 + Klepon Mac:
53 + build:
54 + targets:
55 + Klepon Mac: all
56 + run:
57 + config: Debug
58 + analyze:
59 + config: Debug
60 + archive:
61 + config: Release
62 + profile:
63 + config: Release
64
65 targets:
66 Klepon:
@@ -151,6 +164,70 @@ targets:
164 Release:
165 SWIFT_OPTIMIZATION_LEVEL: -O
166
167 + KleponMac:
168 + name: Klepon Mac
169 + type: application
170 + platform: macOS
171 + sources:
172 + - path: Klepon
173 + info:
174 + path: KleponMac/Info.plist
175 + properties:
176 + CFBundleShortVersionString: $(MARKETING_VERSION)
177 + CFBundleVersion: $(CURRENT_PROJECT_VERSION)
178 + LSApplicationCategoryType: public.app-category.food-and-drink
179 + configFiles:
180 + Debug: Configs/Signing.xcconfig
181 + Release: Configs/Signing.xcconfig
182 + dependencies:
183 + - package: Onde
184 + product: Onde
185 + - sdk: SystemConfiguration.framework
186 + postBuildScripts:
187 + - name: Update CFBundleVersion
188 + shell: /bin/sh
189 + basedOnDependencyAnalysis: false
190 + showEnvVars: false
191 + script: |
192 + set -eu
193 +
194 + SHARED_BUNDLE_VERSION_FILE="${BUILD_DIR}/klepon-cfbundleversion.txt"
195 + if [ -f "${SHARED_BUNDLE_VERSION_FILE}" ]; then
196 + BUNDLE_VERSION="$(cat "${SHARED_BUNDLE_VERSION_FILE}")"
197 + else
198 + BUNDLE_VERSION="$(date -u +"%Y%m%d.%H%M")"
199 + printf '%s' "${BUNDLE_VERSION}" > "${SHARED_BUNDLE_VERSION_FILE}"
200 + fi
201 +
202 + INFO_PLIST="${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
203 +
204 + if [ ! -f "${INFO_PLIST}" ]; then
205 + echo "error: Info.plist not found at ${INFO_PLIST}"
206 + exit 1
207 + fi
208 +
209 + if ! /usr/libexec/PlistBuddy -c "Set :CFBundleVersion ${BUNDLE_VERSION}" "${INFO_PLIST}" >/dev/null 2>&1; then
210 + /usr/libexec/PlistBuddy -c "Add :CFBundleVersion string ${BUNDLE_VERSION}" "${INFO_PLIST}"
211 + fi
212 +
213 + echo "Updated CFBundleVersion to ${BUNDLE_VERSION}"
214 + settings:
215 + base:
216 + PRODUCT_BUNDLE_IDENTIFIER: ai.splitfire.klepon.mac
217 + PRODUCT_NAME: Klepon
218 + MARKETING_VERSION: "1.0.0"
219 + CURRENT_PROJECT_VERSION: 1
220 + ENABLE_USER_SCRIPT_SANDBOXING: NO
221 + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME: AccentColor
222 + CODE_SIGN_ENTITLEMENTS: ""
223 + INFOPLIST_KEY_NSHumanReadableCopyright: "Copyright © 2026 Splitfire AB"
224 + "EXCLUDED_ARCHS[sdk=macosx*]": x86_64
225 + configs:
226 + Debug:
227 + SWIFT_OPTIMIZATION_LEVEL: -Onone
228 + Release:
229 + SWIFT_OPTIMIZATION_LEVEL: -O
230 +
231 KleponWatch:
232 name: Klepon Watch
233 type: application