├── .gitignore ├── LICENSE ├── OllamaGUI ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── ChainIcon.imageset │ │ ├── Contents.json │ │ └── chain.svg │ ├── ChatColor.colorset │ │ └── Contents.json │ ├── ChatHoverColor.colorset │ │ └── Contents.json │ ├── Contents.json │ ├── MicIcon.imageset │ │ ├── Contents.json │ │ └── mic.svg │ ├── MopIcon.imageset │ │ ├── Contents.json │ │ └── mop.svg │ ├── OllamaIcon.imageset │ │ ├── Contents.json │ │ └── ollama.png │ ├── PinIcon.imageset │ │ ├── Contents.json │ │ └── pin.svg │ ├── SettingIcon.imageset │ │ ├── Contents.json │ │ └── settings_FILL0_wght200_GRAD0_opsz24.svg │ ├── SidebarColor.colorset │ │ └── Contents.json │ └── SidebarIconColor.colorset │ │ └── Contents.json ├── Core │ ├── Loadable.swift │ └── NetworkError.swift ├── Data │ ├── AppSettingEntity.swift │ ├── ChatEntity.swift │ ├── MessageEntity.swift │ ├── RoomEntity.swift │ ├── RoomOptionEntity.swift │ └── chat.xcdatamodeld │ │ └── chat.xcdatamodel │ │ └── contents ├── Enum.swift ├── Enum │ ├── RoleEnum.swift │ └── RouterEnum.swift ├── Helper │ ├── DateHelper.swift │ ├── DescribableEnum.swift │ ├── DictionaryEncoder.swift │ ├── HtmlHelper.swift │ ├── IntHelper.swift │ ├── MarkdownTheme.swift │ ├── NetworkHelper.swift │ ├── RandomGenerator.swift │ ├── StringHelper.swift │ ├── ViewHelper.swift │ └── WindowHelper.swift ├── Info.plist ├── Injected │ ├── AppSetting.swift │ ├── DIContainer.swift │ ├── DataInteractor.swift │ ├── Interactor.swift │ └── UpdateTrigger.swift ├── LangChain │ ├── DocumentLoader │ │ ├── BaseLoader.swift │ │ └── HTMLLoader.swift │ ├── Error │ │ └── LangchainError.swift │ ├── Util │ │ └── TextSplitter.swift │ └── VectorStore │ │ └── USearchUtil.swift ├── Model │ ├── ChatModel.swift │ ├── ChatRequestModel.swift │ ├── MessageModel.swift │ ├── ModelDeleteRequestModel.swift │ ├── ModelInfoModel.swift │ ├── ModelList.swift │ ├── OllamaTagModel.swift │ ├── OllmaModel.swift │ ├── OptionModel.swift │ ├── PullRequestModel.swift │ └── PullResponseModel.swift ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── UI │ ├── BubbleShape.swift │ ├── ChatBubble.swift │ ├── ChatHeader.swift │ ├── ChatView.swift │ ├── ChatViewModel.swift │ ├── Commen │ │ ├── ClearButton.swift │ │ ├── CommonButton.swift │ │ ├── Controller │ │ │ └── KeyPressedController.swift │ │ ├── EditorButton.swift │ │ ├── FloatingWindow.swift │ │ ├── SidebarButton.swift │ │ └── XProgressView.swift │ ├── Home │ │ └── HomeView.swift │ ├── LangchainSheet.swift │ ├── Manager │ │ ├── Components │ │ │ ├── LibraryModelListView.swift │ │ │ └── ModelListView.swift │ │ └── ModelManagerView.swift │ ├── MessageEditor.swift │ ├── OverlayToast.swift │ ├── Room │ │ ├── Components │ │ │ └── RoomItem.swift │ │ └── RoomView.swift │ ├── Setting │ │ └── SettingView.swift │ ├── SettingSheet.swift │ ├── SettingSheetViewModel.swift │ ├── SideBar │ │ └── SideBar.swift │ ├── StopButton.swift │ └── Tags │ │ ├── TagListItem.swift │ │ └── TagsView.swift ├── V2 │ ├── Core.swift │ ├── Core │ │ ├── APICall.swift │ │ └── SessionManager.swift │ ├── Data │ │ ├── Datasource │ │ │ ├── OllamaDatasource.swift │ │ │ └── WebDatasource.swift │ │ ├── Dto │ │ │ ├── EmbeddingDto.swift │ │ │ └── EmbeddingResponseDto.swift │ │ └── Repository │ │ │ ├── CrawlingRepositoryImpl.swift │ │ │ └── OllmaRepositoryImpl.swift │ ├── Domain │ │ ├── Repository │ │ │ ├── CrawlingRepository.swift │ │ │ └── OllamaRepository.swift │ │ └── Usecase │ │ │ ├── ChatUsecase.swift │ │ │ └── LangchainUsecase.swift │ └── UI_v2 │ │ ├── Chat │ │ └── Bloc │ │ │ ├── LangchainBloc.swift │ │ │ └── LangchainEvent+State.swift │ │ ├── Root │ │ ├── RootView.swift │ │ └── RootViewModel.swift │ │ ├── SidebarV2 │ │ ├── SidebarV2.swift │ │ └── SidebarV2ViewModel.swift │ │ └── model │ │ └── Embeddings.swift ├── ollamaGUI.entitlements └── ollamaGUIApp.swift ├── OllamaGuiTest ├── Data │ └── Datasource │ │ ├── OllamaDatasource.swift │ │ └── WebDatasource.swift ├── Domain │ └── Usecase │ │ ├── ChatUsecase.swift │ │ ├── Langchain+ChatUsecase.swift │ │ └── LangchainUsecase.swift ├── Info.plist ├── UI │ └── Chat │ │ └── Bloc │ │ └── LangchainBloc.swift └── ollamaGuiTest.swift ├── Podfile ├── Podfile.lock ├── Pods ├── Alamofire │ ├── LICENSE │ ├── README.md │ └── Source │ │ ├── AFError.swift │ │ ├── Alamofire.swift │ │ ├── AlamofireExtended.swift │ │ ├── AuthenticationInterceptor.swift │ │ ├── CachedResponseHandler.swift │ │ ├── Combine.swift │ │ ├── Concurrency.swift │ │ ├── DispatchQueue+Alamofire.swift │ │ ├── EventMonitor.swift │ │ ├── HTTPHeaders.swift │ │ ├── HTTPMethod.swift │ │ ├── MultipartFormData.swift │ │ ├── MultipartUpload.swift │ │ ├── NetworkReachabilityManager.swift │ │ ├── Notifications.swift │ │ ├── OperationQueue+Alamofire.swift │ │ ├── ParameterEncoder.swift │ │ ├── ParameterEncoding.swift │ │ ├── Protected.swift │ │ ├── RedirectHandler.swift │ │ ├── Request.swift │ │ ├── RequestCompression.swift │ │ ├── RequestInterceptor.swift │ │ ├── RequestTaskMap.swift │ │ ├── Response.swift │ │ ├── ResponseSerialization.swift │ │ ├── Result+Alamofire.swift │ │ ├── RetryPolicy.swift │ │ ├── ServerTrustEvaluation.swift │ │ ├── Session.swift │ │ ├── SessionDelegate.swift │ │ ├── StringEncoding+Alamofire.swift │ │ ├── URLConvertible+URLRequestConvertible.swift │ │ ├── URLEncodedFormEncoder.swift │ │ ├── URLRequest+Alamofire.swift │ │ ├── URLSessionConfiguration+Alamofire.swift │ │ └── Validation.swift ├── Manifest.lock ├── Pods.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ └── baesanghwi.xcuserdatad │ │ └── xcschemes │ │ ├── Alamofire.xcscheme │ │ ├── Pods-ollamaGUI.xcscheme │ │ ├── SwiftUIIntrospect.xcscheme │ │ └── xcschememanagement.plist ├── SwiftUIIntrospect │ ├── LICENSE │ ├── README.md │ └── Sources │ │ ├── Introspect.swift │ │ ├── IntrospectableViewType.swift │ │ ├── IntrospectionSelector.swift │ │ ├── IntrospectionView.swift │ │ ├── PlatformVersion.swift │ │ ├── PlatformView.swift │ │ ├── PlatformViewVersion.swift │ │ ├── RuntimeWarnings.swift │ │ ├── Utils.swift │ │ ├── ViewTypes │ │ ├── Button.swift │ │ ├── ColorPicker.swift │ │ ├── DatePicker.swift │ │ ├── DatePickerWithCompactStyle.swift │ │ ├── DatePickerWithFieldStyle.swift │ │ ├── DatePickerWithGraphicalStyle.swift │ │ ├── DatePickerWithStepperFieldStyle.swift │ │ ├── DatePickerWithWheelStyle.swift │ │ ├── Form.swift │ │ ├── FormWithGroupedStyle.swift │ │ ├── FullScreenCover.swift │ │ ├── List.swift │ │ ├── ListCell.swift │ │ ├── ListWithBorderedStyle.swift │ │ ├── ListWithGroupedStyle.swift │ │ ├── ListWithInsetGroupedStyle.swift │ │ ├── ListWithInsetStyle.swift │ │ ├── ListWithSidebarStyle.swift │ │ ├── Map.swift │ │ ├── NavigationSplitView.swift │ │ ├── NavigationStack.swift │ │ ├── NavigationViewWithColumnsStyle.swift │ │ ├── NavigationViewWithStackStyle.swift │ │ ├── PageControl.swift │ │ ├── PickerWithMenuStyle.swift │ │ ├── PickerWithSegmentedStyle.swift │ │ ├── PickerWithWheelStyle.swift │ │ ├── Popover.swift │ │ ├── ProgressViewWithCircularStyle.swift │ │ ├── ProgressViewWithLinearStyle.swift │ │ ├── ScrollView.swift │ │ ├── SearchField.swift │ │ ├── SecureField.swift │ │ ├── Sheet.swift │ │ ├── SignInWithAppleButton.swift │ │ ├── Slider.swift │ │ ├── Stepper.swift │ │ ├── TabView.swift │ │ ├── TabViewWithPageStyle.swift │ │ ├── Table.swift │ │ ├── TextEditor.swift │ │ ├── TextField.swift │ │ ├── TextFieldWithVerticalAxis.swift │ │ ├── Toggle.swift │ │ ├── ToggleWithButtonStyle.swift │ │ ├── ToggleWithCheckboxStyle.swift │ │ ├── ToggleWithSwitchStyle.swift │ │ ├── VideoPlayer.swift │ │ ├── View.swift │ │ ├── ViewController.swift │ │ └── Window.swift │ │ └── Weak.swift └── Target Support Files │ ├── Alamofire │ ├── Alamofire-Info.plist │ ├── Alamofire-dummy.m │ ├── Alamofire-prefix.pch │ ├── Alamofire-umbrella.h │ ├── Alamofire.debug.xcconfig │ ├── Alamofire.modulemap │ └── Alamofire.release.xcconfig │ ├── Pods-ollamaGUI │ ├── Pods-ollamaGUI-Info.plist │ ├── Pods-ollamaGUI-acknowledgements.markdown │ ├── Pods-ollamaGUI-acknowledgements.plist │ ├── Pods-ollamaGUI-dummy.m │ ├── Pods-ollamaGUI-frameworks-Debug-input-files.xcfilelist │ ├── Pods-ollamaGUI-frameworks-Debug-output-files.xcfilelist │ ├── Pods-ollamaGUI-frameworks-Release-input-files.xcfilelist │ ├── Pods-ollamaGUI-frameworks-Release-output-files.xcfilelist │ ├── Pods-ollamaGUI-frameworks.sh │ ├── Pods-ollamaGUI-umbrella.h │ ├── Pods-ollamaGUI.debug.xcconfig │ ├── Pods-ollamaGUI.modulemap │ └── Pods-ollamaGUI.release.xcconfig │ └── SwiftUIIntrospect │ ├── SwiftUIIntrospect-Info.plist │ ├── SwiftUIIntrospect-dummy.m │ ├── SwiftUIIntrospect-prefix.pch │ ├── SwiftUIIntrospect-umbrella.h │ ├── SwiftUIIntrospect.debug.xcconfig │ ├── SwiftUIIntrospect.modulemap │ └── SwiftUIIntrospect.release.xcconfig ├── README.md ├── image ├── 1.1v.png ├── gif.gif ├── langchain.png ├── png.png ├── png2.png └── png3.png ├── ollamaGUI.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcshareddata │ └── xcschemes │ │ └── ollamaGUI.xcscheme └── xcuserdata │ └── baesanghwi.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── ollamaGUI.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ ├── IDEWorkspaceChecks.plist │ └── swiftpm │ └── Package.resolved └── ollamaGUI └── V2 └── Core ├── Error └── MuseError.swift └── Protocol └── BaseBloc.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprintj 10 | *.xccheckout 11 | *.xcconfig 12 | 13 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 14 | build/ 15 | DerivedData/ 16 | *.moved-aside 17 | *.pbxuser 18 | !default.pbxuser 19 | *.mode1v3 20 | !default.mode1v3 21 | *.mode2v3 22 | !default.mode2v3 23 | *.perspectivev3 24 | !default.perspectivev3 25 | 26 | 27 | ## Obj-C/Swift specific 28 | *.hmap 29 | 30 | ## App packaging 31 | *.ipa 32 | *.dSYM.zip 33 | *.dSYM 34 | 35 | ## Playgrounds 36 | timeline.xctimeline 37 | playground.xcworkspace 38 | 39 | # Swift Package Manager 40 | # 41 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 42 | # Packages/ 43 | # Package.pins 44 | # Package.resolved 45 | # *.xcodeproj 46 | # 47 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 48 | # hence it is not needed unless you have added a package configuration file to your project 49 | # .swiftpm 50 | 51 | .build/ 52 | 53 | # CocoaPods 54 | # 55 | # We recommend against adding the Pods directory to your .gitignore. However 56 | # you should judge for yourself, the pros and cons are mentioned at: 57 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 58 | # 59 | # Pods/ 60 | # 61 | # Add this line if you want to avoid checking in source code from the Xcode workspace 62 | # *.xcworkspace 63 | 64 | # Carthage 65 | # 66 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 67 | # Carthage/Checkouts 68 | 69 | Carthage/Build/ 70 | 71 | # Accio dependency management 72 | Dependencies/ 73 | .accio/ 74 | 75 | # fastlane 76 | # 77 | # It is recommended to not store the screenshots in the git repo. 78 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 79 | # For more information about the recommended setup visit: 80 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 81 | 82 | fastlane/report.xml 83 | fastlane/Preview.html 84 | fastlane/screenshots/**/*.png 85 | fastlane/test_output 86 | 87 | # Code Injection 88 | # 89 | # After new code Injection tools there's a generated folder /iOSInjectionProject 90 | # https://github.com/johnno1962/injectionforxcode 91 | 92 | iOSInjectionProject/ 93 | 94 | Pods/ 95 | Podfile.lock -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 enoch1118 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /OllamaGUI/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 | -------------------------------------------------------------------------------- /OllamaGUI/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 | -------------------------------------------------------------------------------- /OllamaGUI/Assets.xcassets/ChainIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "chain.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | }, 21 | "properties" : { 22 | "preserves-vector-representation" : true, 23 | "template-rendering-intent" : "template" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /OllamaGUI/Assets.xcassets/ChainIcon.imageset/chain.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OllamaGUI/Assets.xcassets/ChatColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "38", 9 | "green" : "38", 10 | "red" : "38" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "38", 27 | "green" : "38", 28 | "red" : "38" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /OllamaGUI/Assets.xcassets/ChatHoverColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "46", 9 | "green" : "46", 10 | "red" : "46" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "64", 27 | "green" : "64", 28 | "red" : "64" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /OllamaGUI/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /OllamaGUI/Assets.xcassets/MicIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "mic.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | }, 21 | "properties" : { 22 | "preserves-vector-representation" : true, 23 | "template-rendering-intent" : "template" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /OllamaGUI/Assets.xcassets/MicIcon.imageset/mic.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OllamaGUI/Assets.xcassets/MopIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "mop.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | }, 21 | "properties" : { 22 | "preserves-vector-representation" : true, 23 | "template-rendering-intent" : "template" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /OllamaGUI/Assets.xcassets/MopIcon.imageset/mop.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OllamaGUI/Assets.xcassets/OllamaIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ollama.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /OllamaGUI/Assets.xcassets/OllamaIcon.imageset/ollama.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enoch1118/ollamaGUI/b094a09cff130c60e9c14bf1dcbf0bff1e4020ca/OllamaGUI/Assets.xcassets/OllamaIcon.imageset/ollama.png -------------------------------------------------------------------------------- /OllamaGUI/Assets.xcassets/PinIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "pin.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | }, 21 | "properties" : { 22 | "preserves-vector-representation" : true, 23 | "template-rendering-intent" : "template" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /OllamaGUI/Assets.xcassets/PinIcon.imageset/pin.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OllamaGUI/Assets.xcassets/SettingIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "settings_FILL0_wght200_GRAD0_opsz24.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | }, 21 | "properties" : { 22 | "preserves-vector-representation" : true, 23 | "template-rendering-intent" : "template" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /OllamaGUI/Assets.xcassets/SettingIcon.imageset/settings_FILL0_wght200_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OllamaGUI/Assets.xcassets/SidebarColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "64", 9 | "green" : "64", 10 | "red" : "64" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "64", 27 | "green" : "64", 28 | "red" : "64" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /OllamaGUI/Assets.xcassets/SidebarIconColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "160", 9 | "green" : "160", 10 | "red" : "160" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "160", 27 | "green" : "160", 28 | "red" : "160" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /OllamaGUI/Core/Loadable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Loadable.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/11/24. 6 | // 7 | 8 | import Foundation 9 | 10 | enum Loadable where E: Error { 11 | case initState 12 | case isLoading(last: U?) 13 | case loaded(U) 14 | case cancelled 15 | case finished 16 | case failed(E) 17 | 18 | var value: U? { 19 | switch self { 20 | case let .isLoading(last): return last 21 | case let .loaded(value): return value 22 | default: return nil 23 | } 24 | } 25 | 26 | var error: Error? { 27 | switch self { 28 | case let .failed(error): return error 29 | default: return nil 30 | } 31 | } 32 | 33 | var status: String? { 34 | switch self { 35 | case .initState: return NSLocalizedString("ready to loading", 36 | comment: "") 37 | case .isLoading: return NSLocalizedString("is loading", 38 | comment: "") 39 | case .finished: return NSLocalizedString("complete", 40 | comment: "") 41 | case .cancelled: return NSLocalizedString("user cancelled", comment: "") 42 | case .loaded: return NSLocalizedString("loaded", comment: "") 43 | case let .failed(error): return error.localizedDescription 44 | } 45 | } 46 | } 47 | 48 | extension Loadable: Equatable where U: Equatable { 49 | static func == (lhs: Loadable, rhs: Loadable) -> Bool { 50 | switch (lhs, rhs) { 51 | case (.initState, .initState): return true 52 | case let (.isLoading(lhsC), .isLoading(rhsC)): 53 | return lhsC == rhsC 54 | case let (.loaded(lhsV), .loaded(rhsV)): return lhsV == rhsV 55 | case let (.failed(lhsE), .failed(rhsE)): 56 | return lhsE.localizedDescription == rhsE.localizedDescription 57 | default: return false 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /OllamaGUI/Core/NetworkError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkError.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/11/24. 6 | // 7 | 8 | import Alamofire 9 | import Foundation 10 | 11 | enum NetworkError: Error { 12 | case disconnected 13 | case commonError(error: AFError) 14 | case badRequest(error: Error?) 15 | case inValidStatus(status: Int) 16 | case jsonEncodeError 17 | } 18 | 19 | extension NetworkError { 20 | var localizedDescription: String { 21 | switch self { 22 | case let .badRequest(error): 23 | return NSLocalizedString( 24 | "bad request \(error?.localizedDescription ?? "no data")", 25 | comment: "" 26 | ) 27 | case let .inValidStatus(status): 28 | return NSLocalizedString( 29 | "invalid status code \(status)", 30 | comment: "" 31 | ) 32 | case .disconnected: 33 | return NSLocalizedString("network error", comment: "") 34 | case let .commonError(error): 35 | return NSLocalizedString("error \(error)", comment: "") 36 | case .jsonEncodeError: 37 | return NSLocalizedString("error to encode json", comment: "") 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /OllamaGUI/Data/AppSettingEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppSettingEntity.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/18/24. 6 | // 7 | 8 | import Foundation 9 | import SwiftData 10 | 11 | @Model 12 | class AppSettingEntity { 13 | var baseUrl:String 14 | var selectedModel:String 15 | 16 | init(baseUrl: String, selectedModel: String) { 17 | self.baseUrl = baseUrl 18 | self.selectedModel = selectedModel 19 | } 20 | } 21 | 22 | 23 | extension AppSettingEntity { 24 | static var `default`:AppSettingEntity { 25 | AppSettingEntity(baseUrl: "http://localhost:11434", selectedModel: "llama2") 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /OllamaGUI/Data/ChatEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChatEntity.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/15/24. 6 | // 7 | 8 | import Foundation 9 | import SwiftData 10 | 11 | // 12 | // var message: MessageModel? 13 | // let stream: Bool? 14 | // let done: Bool? 15 | // let createdAt: Date? 16 | // let images: [String]? 17 | // let model: String? 18 | // 19 | @Model 20 | class ChatEntity { 21 | @Relationship(deleteRule: .cascade) var message: MessageEntity? 22 | var createdAt: Date 23 | 24 | init(message: MessageEntity? = nil, createdAt: Date) { 25 | self.message = message 26 | self.createdAt = createdAt 27 | } 28 | } 29 | 30 | #if DEBUG 31 | extension ChatEntity { 32 | static var randomChat: ChatEntity { 33 | return ChatEntity(message: nil,createdAt: RandomGenerator.randomDate()) 34 | } 35 | } 36 | #endif 37 | -------------------------------------------------------------------------------- /OllamaGUI/Data/MessageEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChatEntity.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/15/24. 6 | // 7 | 8 | import Foundation 9 | import SwiftData 10 | 11 | // var id: UUID 12 | // var role: RoleEnum 13 | // var content: String 14 | // var images: [String]? 15 | @Model 16 | class MessageEntity { 17 | var role: RoleEnum 18 | var content: String 19 | var images: [Data]? 20 | 21 | init(role: RoleEnum, content: String, images: [Data]? = nil) { 22 | self.role = role 23 | self.content = content 24 | self.images = images 25 | } 26 | } 27 | 28 | #if DEBUG 29 | extension MessageEntity { 30 | static var randomMessage: MessageEntity { 31 | let rols: [RoleEnum] = [.user, .assistant] 32 | return MessageEntity( 33 | role: rols.randomElement()!, 34 | content: RandomGenerator.randomNames.randomElement()! 35 | ) 36 | } 37 | } 38 | #endif 39 | -------------------------------------------------------------------------------- /OllamaGUI/Data/RoomEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RoomEntity.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/15/24. 6 | // 7 | 8 | import Foundation 9 | import SwiftData 10 | 11 | @Model 12 | class RoomEntity { 13 | var updatedAt: Date 14 | @Relationship(deleteRule: .cascade) var chats: [ChatEntity] 15 | var title: String? 16 | @Relationship(deleteRule: .cascade) var option: RoomOptionEntity? 17 | 18 | 19 | init(updatedAt: Date, chats: [ChatEntity], title: String? = nil) { 20 | self.updatedAt = updatedAt 21 | self.chats = chats 22 | self.title = title ?? "new chat" 23 | } 24 | 25 | init() { 26 | updatedAt = Date.now 27 | chats = [] 28 | title = "new chat" 29 | } 30 | 31 | var getChatModel: [ChatModel] { 32 | chats.map { ChatModel(entity: $0) } 33 | } 34 | 35 | func clean() { 36 | chats.removeAll() 37 | } 38 | } 39 | 40 | extension [ChatModel] { 41 | var toEntity: [ChatEntity] { 42 | map { $0.toEntity } 43 | } 44 | } 45 | 46 | extension ChatModel { 47 | var toEntity: ChatEntity { 48 | ChatEntity( 49 | message: MessageEntity(role: message!.role, 50 | content: message!.content, images: []), 51 | createdAt: createdAt ?? Date.now 52 | ) 53 | } 54 | } 55 | 56 | extension RoomEntity { 57 | static var randomRoom: RoomEntity { 58 | let room = RoomEntity() 59 | room.title = RandomGenerator.randomNames.randomElement() 60 | room.chats = [ 61 | ] 62 | 63 | room.updatedAt = RandomGenerator.randomDate() 64 | return room 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /OllamaGUI/Data/RoomOptionEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RoomOption.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/5/24. 6 | // 7 | 8 | import Foundation 9 | import SwiftData 10 | 11 | @Model 12 | class RoomOptionEntity { 13 | var model: String? 14 | var system: String? 15 | var top_p: Float 16 | var top_k: Int 17 | var temperature: Float? 18 | init(model: String?, 19 | system: String?, 20 | top_p: Float = 0.9, 21 | top_k: Int = 40, 22 | temperature: Float = 0.8 23 | ) { 24 | 25 | self.model = model 26 | self.system = system 27 | self.top_p = top_p 28 | self.top_k = top_k 29 | self.temperature = temperature 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /OllamaGUI/Data/chat.xcdatamodeld/chat.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /OllamaGUI/Enum.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Enum.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/10/24. 6 | // 7 | 8 | import Foundation 9 | -------------------------------------------------------------------------------- /OllamaGUI/Enum/RoleEnum.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RoleEnum.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/10/24. 6 | // 7 | 8 | import Foundation 9 | 10 | enum RoleEnum:String,Codable{ 11 | case user = "user" 12 | case assistant = "assistant" 13 | case system = "system" 14 | } 15 | 16 | extension RoleEnum { 17 | var value:String{ 18 | String(describing: self) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /OllamaGUI/Enum/RouterEnum.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouterEnum.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/11/24. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | -------------------------------------------------------------------------------- /OllamaGUI/Helper/DateHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DateHelper.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/17/24. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Date { 11 | func fullDistance( 12 | from date: Date, 13 | resultIn component: Calendar.Component, 14 | calendar: Calendar = .current 15 | ) -> Int? { 16 | calendar.dateComponents([component], from: self, to: date) 17 | .value(for: component) 18 | } 19 | 20 | func distance( 21 | from date: Date, 22 | only component: Calendar.Component, 23 | calendar: Calendar = .current 24 | ) -> Int { 25 | let days1 = calendar.component(component, from: self) 26 | let days2 = calendar.component(component, from: date) 27 | return days1 - days2 28 | } 29 | 30 | func hasSame(_ component: Calendar.Component, as date: Date) -> Bool { 31 | distance(from: date, only: component) == 0 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /OllamaGUI/Helper/DescribableEnum.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringConvertableEnum.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/11/24. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol DescribableEnum{ 11 | var description:String? { get } 12 | } 13 | -------------------------------------------------------------------------------- /OllamaGUI/Helper/DictionaryEncoder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DictionaryEncoder.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/12/24. 6 | // 7 | 8 | import Foundation 9 | 10 | class DictionaryEncoder { 11 | private let encoder = JSONEncoder() 12 | 13 | func encode(_ value: T) throws -> [String: Any] where T : Encodable { 14 | let data = try encoder.encode(value) 15 | return try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String: Any] 16 | } 17 | } 18 | 19 | 20 | extension Encodable{ 21 | func getLeafDictionary() -> [String:Any]?{ 22 | let data = try? JSONEncoder().encode(self) 23 | let dict = try? JSONSerialization.jsonObject(with: data!) as? [String: Any] 24 | return dict 25 | } 26 | } 27 | 28 | protocol DictionaryEncodable{ 29 | func getDictionary()->[String:Any]? 30 | } 31 | -------------------------------------------------------------------------------- /OllamaGUI/Helper/IntHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IntHelper.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | extension Int{ 12 | var getSize:String{ 13 | let div = 1024.0 14 | if self > Int(pow(div,3)){ 15 | let val = pow(div,3) 16 | return "\((Double(self) / val).roundToDecimal(2))GB" 17 | } 18 | if self > Int(pow(div,2)){ 19 | let val = pow(div,3) 20 | return "\((Double(self) / val).roundToDecimal(2))MB" 21 | } 22 | if self > Int(pow(div,1)){ 23 | let val = pow(div,2) 24 | return "\((Double(self) / val).roundToDecimal(2))KB" 25 | } 26 | if self > Int(pow(div,0)){ 27 | let val = pow(div,1) 28 | return "\((Double(self) / val).roundToDecimal(2))B" 29 | } 30 | 31 | return "cal error" 32 | } 33 | } 34 | 35 | 36 | extension Double { 37 | func roundToDecimal(_ fractionDigits: Int) -> Double { 38 | let multiplier = pow(10, Double(fractionDigits)) 39 | return Darwin.round(self * multiplier) / multiplier 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /OllamaGUI/Helper/MarkdownTheme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MarkdownTheme.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/15/24. 6 | // 7 | 8 | import Foundation 9 | import MarkdownUI 10 | import SwiftUI 11 | 12 | extension Theme { 13 | static let user = Theme() 14 | .code { 15 | FontFamilyVariant(.monospaced) 16 | FontSize(.em(0.85)) 17 | } 18 | .link { 19 | ForegroundColor(.purple) 20 | } 21 | .paragraph { configuration in 22 | configuration.label 23 | .relativeLineSpacing(.em(0.25)) 24 | .markdownMargin(top: 0, bottom: 16) 25 | } 26 | .listItem { configuration in 27 | configuration.label 28 | .markdownMargin(top: .em(0.25)) 29 | } 30 | .text { 31 | BackgroundColor(.blue) 32 | } 33 | 34 | static let assistant = Theme() 35 | .code { 36 | FontFamilyVariant(.monospaced) 37 | FontSize(.em(0.85)) 38 | }.codeBlock { conf in 39 | conf.label 40 | .padding(.horizontal, 16) 41 | .padding(.vertical, 20) 42 | .frame(maxWidth: .infinity,alignment: .leading) 43 | .padding(.top,14) 44 | 45 | .markdownTextStyle { 46 | ForegroundColor(.white) 47 | } 48 | .background(RoundedRectangle(cornerRadius: 8).fill(.black)) 49 | .overlay(alignment: .topTrailing) { 50 | Button (action: { 51 | let pasteboard = NSPasteboard.general 52 | pasteboard.clearContents() 53 | pasteboard.setString(conf.content, forType: .string) 54 | print(conf.content) 55 | }) { 56 | Image(systemName: "doc.on.doc") 57 | }.padding() 58 | } 59 | } 60 | .link { 61 | ForegroundColor(.purple) 62 | } 63 | .paragraph { configuration in 64 | configuration.label 65 | .relativeLineSpacing(.em(0.25)) 66 | .markdownMargin(top: 0, bottom: 16) 67 | } 68 | .listItem { configuration in 69 | configuration.label 70 | .markdownMargin(top: .em(0.25)) 71 | } 72 | .text { 73 | BackgroundColor(.white) 74 | ForegroundColor(.black) 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /OllamaGUI/Helper/RandomGenerator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RandomGenerator.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/17/24. 6 | // 7 | 8 | import Foundation 9 | 10 | enum RandomGenerator { 11 | static var randomNames: [String] { 12 | [ 13 | "Unboxing the Secrets of [Mysterious Subject]: A Journey of Discovery", 14 | "From Novice to Ninja: Mastering the Art of [Intriguing Skill]", 15 | "Beyond the Surface: A Deep Dive into the World of [Unfamiliar Topic]", 16 | "Confessions of a [Unexpected Profession]: The Untold Stories of [Intriguing Aspect]", 17 | "The Unexpected Twists and Turns of [Intriguing Event]: A Story You Won't Believe", 18 | "Cracking the Code: Unraveling the Mysteries of [Complex Phenomenon]", 19 | "From Zero to Hero: My Personal Quest to Conquering [Daunting Challenge]", 20 | "Inside the Minds of [Fascinating Group]: A Glimpse into Their [Unique Perspective]", 21 | "The Forgotten Tale of [Historical Event or Figure]: A Retelling for the Modern Age", 22 | "Navigating the Labyrinth: A Guide to Surviving [Challenging Situation]", 23 | ] 24 | } 25 | 26 | static func randomDate(in range: Range = (Date.now - 100000).. Date { 27 | Date( 28 | timeIntervalSinceNow: .random( 29 | in: range.lowerBound.timeIntervalSinceNow...range.upperBound.timeIntervalSinceNow 30 | ) 31 | ) 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /OllamaGUI/Helper/StringHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringHelper.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/15/24. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | extension String { 12 | var removeFirstBreakLine: String { 13 | if starts(with: "\n") { 14 | return replacing("\n", with: "", maxReplacements: 1) 15 | } 16 | return self 17 | } 18 | 19 | var verifyUrl: Bool { 20 | let urlPattern = 21 | "https?://[localhost|(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b]([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)" 22 | let urlRegex = try! NSRegularExpression(pattern: urlPattern) 23 | if urlRegex.firstMatch( 24 | in: self, 25 | options: [], 26 | range: NSRange(location: 0, length: utf16.count) 27 | ) != nil { 28 | return true 29 | } else { 30 | return false 31 | } 32 | } 33 | 34 | var base64Data: Data? { 35 | let utf8Data = data(using: .utf8) 36 | return utf8Data?.base64EncodedData() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /OllamaGUI/Helper/ViewHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewHelper.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/9/24. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct PlaceHolder: ViewModifier { 12 | var placeHolder: T 13 | var show: Bool 14 | func body(content: Content) -> some View { 15 | ZStack(alignment: .leading) { 16 | if show { VStack { 17 | placeHolder.padding(.leading,5) 18 | Spacer() 19 | } } 20 | content 21 | } 22 | } 23 | } 24 | 25 | extension View { 26 | func placeHolder(_ holder: T, show: Bool) -> some View { 27 | modifier(PlaceHolder(placeHolder: holder, show: show)) 28 | } 29 | 30 | var textLeft:some View { 31 | self.frame(maxWidth: .infinity,alignment: .leading) 32 | } 33 | 34 | var flexTL:some View{ 35 | self.frame(maxWidth: .infinity,maxHeight: .infinity,alignment: .topLeading) 36 | } 37 | var flexC:some View{ 38 | self.frame(maxWidth: .infinity,maxHeight: .infinity,alignment: .center) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /OllamaGUI/Helper/WindowHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WindowHelper.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/18/24. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct WindowAccessor: NSViewRepresentable { 12 | @Binding var window: NSWindow? 13 | 14 | func makeNSView(context _: Context) -> NSView { 15 | let view = NSView() 16 | DispatchQueue.main.async { 17 | self.window = view.window 18 | } 19 | return view 20 | } 21 | 22 | func updateNSView(_: NSView, context _: Context) {} 23 | } 24 | -------------------------------------------------------------------------------- /OllamaGUI/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSAppTransportSecurity 6 | 7 | NSExceptionDomains 8 | 9 | ollama.ai 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /OllamaGUI/Injected/AppSetting.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppSetting.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/15/24. 6 | // 7 | 8 | import Combine 9 | import Foundation 10 | 11 | class AppSetting { 12 | private var subject = CurrentValueSubject(nil) 13 | var ollamaDs: OllamaDatasource 14 | 15 | init(ollamaDs: OllamaDatasource) { 16 | self.ollamaDs = ollamaDs 17 | } 18 | 19 | private var entity: AppSettingEntity { 20 | subject.value! 21 | } 22 | 23 | func updateBaseUrl(_ url: String) { 24 | let entity = entity 25 | entity.baseUrl = url 26 | ollamaDs.baseUrl = url 27 | subject.send(entity) 28 | } 29 | 30 | func updateModel(_ model: String) { 31 | let entity = entity 32 | entity.selectedModel = model 33 | subject.send(entity) 34 | } 35 | 36 | func updateSetting(_ entity: AppSettingEntity) { 37 | print("setting setted") 38 | print(ollamaDs.baseUrl) 39 | ollamaDs.baseUrl = entity.baseUrl 40 | subject.send(entity) 41 | } 42 | 43 | var baseUrl: String { 44 | entity.baseUrl 45 | } 46 | 47 | var model: String { 48 | entity.selectedModel 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /OllamaGUI/Injected/DataInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataInteractor.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/15/24. 6 | // 7 | 8 | import Foundation 9 | import SwiftData 10 | 11 | protocol DataInteractor { 12 | func fetchRoom(context: ModelContext) -> [RoomEntity] 13 | 14 | func clearRoom(context: ModelContext, room: RoomEntity) 15 | 16 | func insertRoom(context: ModelContext) -> PersistentIdentifier 17 | 18 | func deleteRoom(context: ModelContext, room: RoomEntity) 19 | 20 | func fetchSetting(context: ModelContext) -> AppSettingEntity 21 | } 22 | 23 | struct RealDataInteractor: DataInteractor { 24 | func insertRoom(context: ModelContext) -> PersistentIdentifier { 25 | let room = RoomEntity() 26 | context.insert(room) 27 | return room.id 28 | } 29 | func fetchRoom(context: ModelContext) -> [RoomEntity] { 30 | let des = FetchDescriptor() 31 | var rooms = try? context.fetch(des) 32 | if rooms == nil || rooms!.isEmpty { 33 | context.insert(RoomEntity()) 34 | rooms = try? context.fetch(des) 35 | } 36 | if rooms == nil || rooms!.isEmpty { 37 | fatalError() 38 | } 39 | return rooms!.sorted(by: { $0.updatedAt > $1.updatedAt }) 40 | } 41 | 42 | func clearRoom(context: ModelContext, room: RoomEntity) { 43 | for message in room.chats { 44 | context.delete(message) 45 | } 46 | } 47 | 48 | func deleteRoom(context: ModelContext, room: RoomEntity) { 49 | context.delete(room) 50 | } 51 | 52 | func fetchSetting(context: ModelContext) -> AppSettingEntity { 53 | let des = FetchDescriptor() 54 | var setting = try? context.fetch(des) 55 | if setting?.first == nil { 56 | context.insert(AppSettingEntity.default) 57 | setting = try? context.fetch(des) 58 | } 59 | if setting?.first == nil { 60 | fatalError() 61 | } 62 | return setting!.first! 63 | } 64 | 65 | } 66 | 67 | struct StubDataInteractor: DataInteractor { 68 | func fetchRoom(context _: ModelContext) -> [RoomEntity] { 69 | [.randomRoom, .randomRoom, .randomRoom] 70 | } 71 | 72 | func clearRoom(context _: ModelContext, room _: RoomEntity) {} 73 | 74 | func insertRoom(context _: ModelContext) -> PersistentIdentifier { 75 | let room = RoomEntity() 76 | return room.id 77 | } 78 | 79 | func deleteRoom(context _: ModelContext, room _: RoomEntity) {} 80 | 81 | func fetchSetting(context: ModelContext) -> AppSettingEntity { 82 | .default 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /OllamaGUI/Injected/UpdateTrigger.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UpdateTrigger.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/18/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | 12 | struct UpdateTrigger { 13 | var subject = CurrentValueSubject(.init()) 14 | 15 | var publisher: AnyPublisher { 16 | subject.eraseToAnyPublisher() 17 | } 18 | 19 | func triggerNewMessage() { 20 | var current = subject.value 21 | current.newMessage = true 22 | subject.send(current) 23 | } 24 | 25 | func newMessageHandled() { 26 | var current = subject.value 27 | current.newMessage = false 28 | subject.send(current) 29 | } 30 | 31 | func triggerNewModel(){ 32 | var current = subject.value 33 | current.newModel = true 34 | subject.send(current) 35 | } 36 | 37 | func newModelHandled(){ 38 | var current = subject.value 39 | current.newModel = false 40 | subject.send(current) 41 | } 42 | 43 | var hasNewMessage:Bool { 44 | subject.value.newMessage 45 | } 46 | var hasNewModel:Bool { 47 | subject.value.newModel 48 | } 49 | 50 | } 51 | 52 | 53 | 54 | struct TriggerModel{ 55 | var newMessage = false 56 | var newModel = false 57 | } 58 | 59 | 60 | extension TriggerModel{ 61 | static fileprivate var `default`:Self{ 62 | Self.init() 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /OllamaGUI/LangChain/DocumentLoader/BaseLoader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseLoader.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/7/24. 6 | // 7 | import Foundation 8 | struct Document: Equatable,Codable { 9 | public init(page_content: String, metadata: [String: String]) { 10 | self.page_content = page_content 11 | self.metadata = metadata 12 | } 13 | 14 | public let page_content: String 15 | public var metadata: [String: String] 16 | public static func == (lhs: Document, rhs: Document) -> Bool { 17 | return lhs.page_content == rhs.page_content 18 | } 19 | 20 | 21 | enum CodingKeys: CodingKey { 22 | case page_content 23 | case metadata 24 | } 25 | 26 | init(from decoder: any Decoder) throws { 27 | let container = try decoder.container(keyedBy: CodingKeys.self) 28 | self.page_content = try container.decode(String.self, forKey: .page_content) 29 | self.metadata = try container.decode([String : String].self, forKey: .metadata) 30 | } 31 | 32 | func encode(to encoder: any Encoder) throws { 33 | var container = encoder.container(keyedBy: CodingKeys.self) 34 | try container.encode(self.page_content, forKey: .page_content) 35 | try container.encode(self.metadata, forKey: .metadata) 36 | } 37 | } 38 | 39 | class BaseLoader { 40 | public func load() async -> [Document] { 41 | let type = type() 42 | do { 43 | let docs = try await _load() 44 | return docs 45 | } catch let LangchainError.loadError(message) { 46 | print("Catch langchain loader error \(type):\(message)") 47 | return [] 48 | } catch { 49 | print("Catch other error \(type)") 50 | return [] 51 | } 52 | } 53 | 54 | func _load() async throws -> [Document] { 55 | [] 56 | } 57 | 58 | func type() -> String { 59 | "Base" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /OllamaGUI/LangChain/DocumentLoader/HTMLLoader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HTMLLoader.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/7/24. 6 | // 7 | 8 | import Foundation 9 | import SwiftSoup 10 | 11 | class HTMLLoader: BaseLoader { 12 | let html: String 13 | let url: String 14 | 15 | public init(html: String, url: String) { 16 | self.html = html 17 | self.url = url 18 | } 19 | 20 | override func _load() async throws -> [Document] { 21 | do { 22 | let doc: SwiftSoup.Document = try SwiftSoup.parse(html) 23 | let text = try doc.text() 24 | let metadata: [String: String] = ["url": url] 25 | return [Document(page_content: text, metadata: metadata)] 26 | } catch let Exception.Error(_, message) { 27 | throw LangchainError 28 | .loadError(message: "parse html fail with \(message)") 29 | } catch { 30 | throw LangchainError 31 | .loadError(message: "parse html fail with \(error)") 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /OllamaGUI/LangChain/Error/LangchainError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LangchainError.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/7/24. 6 | // 7 | 8 | import Foundation 9 | 10 | enum LangchainError:Error { 11 | case loadError(message: String) 12 | } 13 | -------------------------------------------------------------------------------- /OllamaGUI/Model/ChatRequestModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChatRequestModel.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/12/24. 6 | // 7 | 8 | import Foundation 9 | 10 | struct ChatRequestModel: Encodable, DictionaryEncodable { 11 | var model: String 12 | var messages: [MessageModel] 13 | var stream: Bool 14 | var format: String 15 | var options: OptionModel? 16 | 17 | init(model: String, messages: [MessageModel], stream: Bool) { 18 | self.model = model 19 | self.messages = messages 20 | self.stream = stream 21 | self.format = "json" 22 | } 23 | 24 | enum CodingKeys: CodingKey { 25 | case model 26 | case messages 27 | case stream 28 | case options 29 | } 30 | 31 | func encode(to encoder: Encoder) throws { 32 | var container = encoder.container(keyedBy: CodingKeys.self) 33 | try container.encode(model, forKey: .model) 34 | try container.encode(stream, forKey: .stream) 35 | try container.encode(messages, forKey: .messages) 36 | try container.encodeIfPresent(options, forKey: .options) 37 | } 38 | 39 | func getDictionary() -> [String: Any]? { 40 | let dict = getLeafDictionary() 41 | guard var d = dict else { 42 | return nil 43 | } 44 | var list: [[String: Any]] = [] 45 | for m in messages { 46 | let val = m.getLeafDictionary() 47 | if val == nil { 48 | continue 49 | } 50 | list.append(val!) 51 | } 52 | d["messages"] = list 53 | d["options"] = options.getLeafDictionary() 54 | return d 55 | } 56 | 57 | init(of: MessageModel, stream: Bool = false) { 58 | model = "llama2" 59 | self.stream = stream 60 | messages = [of] 61 | options = nil 62 | format = "json" 63 | } 64 | 65 | init(ofList: [MessageModel], stream: Bool = false) { 66 | model = "llama2" 67 | self.stream = stream 68 | messages = ofList 69 | format = "json" 70 | } 71 | 72 | 73 | mutating func applyOption(option: RoomOptionEntity?) -> ChatRequestModel { 74 | guard let option = option else { 75 | return self 76 | } 77 | options = OptionModel(top_p: option.top_p, 78 | top_k: option.top_k, 79 | temperature: option.temperature) 80 | 81 | if let model = option.model { 82 | self.model = model 83 | } 84 | return self 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /OllamaGUI/Model/MessageModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MessageModel.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/12/24. 6 | // 7 | 8 | import Foundation 9 | 10 | struct MessageModel: Codable, Hashable { 11 | var id: UUID 12 | var role: RoleEnum 13 | var content: String 14 | var images: [String]? 15 | 16 | init(from decoder: Decoder) throws { 17 | let container = try decoder.container(keyedBy: CodingKeys.self) 18 | id = UUID() 19 | role = try container.decode(RoleEnum.self, forKey: .role) 20 | content = try container.decode(String.self, forKey: .content) 21 | images = try container.decodeIfPresent([String].self, forKey: .images) 22 | } 23 | 24 | enum CodingKeys: CodingKey { 25 | case role 26 | case content 27 | case images 28 | } 29 | 30 | func encode(to encoder: Encoder) throws { 31 | var container = encoder.container(keyedBy: CodingKeys.self) 32 | try container.encode(role, forKey: .role) 33 | try container.encode(content, forKey: .content) 34 | try container.encodeIfPresent(images, forKey: .images) 35 | } 36 | 37 | init(text: String, role: RoleEnum = .user, images: [String]? = nil) { 38 | id = UUID() 39 | content = text 40 | self.role = role 41 | self.images = images 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /OllamaGUI/Model/ModelDeleteRequestModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModelDeleteRequestModel.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/22/24. 6 | // 7 | 8 | import Foundation 9 | 10 | -------------------------------------------------------------------------------- /OllamaGUI/Model/ModelInfoModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModelInfoModel.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/19/24. 6 | // 7 | 8 | import Foundation 9 | 10 | struct ModelInfoModel: Decodable, Hashable { 11 | var id: UUID 12 | var name: String 13 | var modifiedAt: Date 14 | var size: Int 15 | 16 | enum CodingKeys: String, CodingKey { 17 | case name 18 | case modifiedAt = "modified_at" 19 | case size 20 | } 21 | 22 | init(from decoder: Decoder) throws { 23 | let container = try decoder.container(keyedBy: CodingKeys.self) 24 | id = UUID() 25 | name = try container.decode(String.self, forKey: .name) 26 | modifiedAt = try container.decode(Date.self, forKey: .modifiedAt) 27 | size = try container.decode(Int.self, forKey: .size) 28 | } 29 | 30 | init(name: String, modifiedAt: Date, size: Int) { 31 | id = UUID() 32 | self.name = name 33 | self.modifiedAt = modifiedAt 34 | self.size = size 35 | } 36 | } 37 | 38 | #if DEBUG 39 | extension ModelInfoModel { 40 | static var preview: ModelInfoModel { 41 | Self(name: "testModel", modifiedAt: .now, size: 123_242_425) 42 | } 43 | } 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /OllamaGUI/Model/ModelList.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModelList.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/19/24. 6 | // 7 | 8 | import Foundation 9 | 10 | struct ModelList: Decodable{ 11 | var models: [ModelInfoModel] 12 | 13 | enum CodingKeys: CodingKey { 14 | case models 15 | } 16 | 17 | 18 | init(from decoder: Decoder) throws { 19 | let container = try decoder.container(keyedBy: CodingKeys.self) 20 | self.models = try container.decode([ModelInfoModel].self, forKey: .models) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /OllamaGUI/Model/OllamaTagModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OllamaTagModel.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | struct OllamaTagModel { 11 | var id: UUID 12 | var title: String 13 | var size: String 14 | var parent: String 15 | var sha: String 16 | var updatedAt: String 17 | 18 | init(title: String, size: String, sha: String, updatedAt: String,parent:String) { 19 | self.id = UUID() 20 | self.title = title 21 | self.size = size 22 | self.sha = sha 23 | self.updatedAt = updatedAt 24 | self.parent = parent 25 | } 26 | } 27 | 28 | extension OllamaTagModel { 29 | init() { 30 | title = "tag" 31 | size = "9.999TB" 32 | sha = "12i391239i12j4i" 33 | updatedAt = "long long ago" 34 | parent = "llama2" 35 | id = UUID() 36 | } 37 | 38 | static func guardedInit( 39 | title: String?, 40 | size: String?, 41 | sha: String?, 42 | updatedAt: String?, 43 | parent:String 44 | ) -> OllamaTagModel? { 45 | if title != nil, size != nil, sha != nil, updatedAt != nil { 46 | return Self( 47 | title: title!, 48 | size: size!, 49 | sha: sha!, 50 | updatedAt: updatedAt!, 51 | parent: parent 52 | ) 53 | } 54 | return nil 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /OllamaGUI/Model/OllmaModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OllmaModel.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | struct OllamaModel { 11 | var id:UUID = UUID() 12 | var title: String 13 | var desc: String 14 | var pulls: String 15 | var tagsCount: String 16 | var updatedAt: String 17 | 18 | init( 19 | title: String, 20 | desc: String, 21 | pulls: String, 22 | tagsCount: String, 23 | updatedAt: String 24 | ) { 25 | self.title = title 26 | self.desc = desc 27 | self.pulls = pulls 28 | self.tagsCount = tagsCount 29 | self.updatedAt = updatedAt 30 | } 31 | } 32 | 33 | extension OllamaModel { 34 | static func guardedInit( 35 | title: String?, 36 | desc: String?, 37 | pulls: String?, 38 | tagCount: String?, 39 | updatedAt: String? 40 | ) -> OllamaModel? { 41 | if title != nil, desc != nil, pulls != nil, tagCount != nil, 42 | updatedAt != nil 43 | { 44 | return Self( 45 | title: title!, 46 | desc: desc!, 47 | pulls: pulls!, 48 | tagsCount: tagCount!, 49 | updatedAt: updatedAt! 50 | ) 51 | } 52 | return nil 53 | } 54 | 55 | init() { 56 | title = "ollmaa" 57 | desc = "awesome model" 58 | pulls = "many pulls" 59 | tagsCount = "many tags" 60 | updatedAt = "2 weeks ago" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /OllamaGUI/Model/OptionModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OptionModel.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/5/24. 6 | // 7 | 8 | import Foundation 9 | 10 | struct OptionModel:Encodable,DictionaryEncodable { 11 | var top_p: Float 12 | var top_k: Int 13 | var temperature: Float? 14 | 15 | init(top_p: Float, top_k: Int,temperature:Float?) { 16 | self.top_p = top_p 17 | self.top_k = top_k 18 | self.temperature = temperature 19 | } 20 | 21 | enum CodingKeys: CodingKey { 22 | case top_p 23 | case top_k 24 | case temperature 25 | } 26 | 27 | func encode(to encoder: Encoder) throws { 28 | var container = encoder.container(keyedBy: CodingKeys.self) 29 | try container.encode(self.top_p, forKey: .top_p) 30 | try container.encode(self.top_k, forKey: .top_k) 31 | try container.encodeIfPresent(self.temperature, forKey: .temperature) 32 | } 33 | 34 | 35 | func getDictionary() -> [String : Any]? { 36 | let dict = getLeafDictionary() 37 | guard let d = dict else { 38 | return nil 39 | } 40 | return d 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /OllamaGUI/Model/PullRequestModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PullRequestModel.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/22/24. 6 | // 7 | 8 | import Foundation 9 | 10 | struct PullRequestModel: Encodable { 11 | var name: String 12 | var stream: Bool 13 | 14 | enum CodingKeys: CodingKey { 15 | case name 16 | } 17 | 18 | init(name: String) { 19 | self.name = name 20 | stream = true 21 | } 22 | 23 | func encode(to encoder: Encoder) throws { 24 | var container = encoder.container(keyedBy: CodingKeys.self) 25 | try container.encode(self.name, forKey: .name) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /OllamaGUI/Model/PullResponseModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PullResponseModel.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/22/24. 6 | // 7 | 8 | import Foundation 9 | 10 | struct PullResponseModel: Decodable { 11 | var status:String 12 | var digest:String? 13 | var total:Double? 14 | var completed:Double? 15 | 16 | init(status: String, digest: String? = nil, total: Double? = nil, completed: Double? = nil) { 17 | self.status = status 18 | self.digest = digest 19 | self.total = total 20 | self.completed = completed 21 | } 22 | 23 | enum CodingKeys: CodingKey { 24 | case status 25 | case digest 26 | case total 27 | case completed 28 | } 29 | 30 | 31 | init(from decoder: Decoder) throws { 32 | let container = try decoder.container(keyedBy: CodingKeys.self) 33 | self.status = try container.decode(String.self, forKey: .status) 34 | self.digest = try container.decodeIfPresent(String.self, forKey: .digest) 35 | self.total = try container.decodeIfPresent(Double.self, forKey: .total) 36 | self.completed = try container.decodeIfPresent(Double.self, forKey: .completed) 37 | } 38 | } 39 | 40 | 41 | extension PullResponseModel { 42 | var progress:Float? { 43 | if total == nil || completed == nil { 44 | return nil 45 | } 46 | return Float(completed!) / Float(total!) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /OllamaGUI/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /OllamaGUI/UI/BubbleShape.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BubbleShape.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/10/24. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct BubbleShape: Shape { 12 | let role: RoleEnum 13 | 14 | func path(in rect: CGRect) -> Path { 15 | return (role == .user) ? rightBubble(in: rect) : leftBubble(in: rect) 16 | } 17 | 18 | func rightBubble(in rect: CGRect) -> Path { 19 | let w = rect.width 20 | let h = rect.height 21 | 22 | let path = Path { p in 23 | p.move(to: CGPoint(x: 16, y: 16)) 24 | p.addCurve(to: CGPoint(x: 32, y: 0), 25 | control1: CGPoint(x: 16, y: 8), 26 | control2: CGPoint(x: 24, y: 0)) 27 | p.addLine(to: CGPoint(x: w - 8, y: 0)) 28 | p.addLine(to: CGPoint(x: w - 16, y: 8)) 29 | p.addLine(to: CGPoint(x: w - 16, y: h - 16)) 30 | 31 | p.addCurve(to: CGPoint(x: w - 32, y: h), 32 | control1: CGPoint(x: w - 16, y: h - 8), 33 | control2: CGPoint(x: w - 24, y: h)) 34 | 35 | p.addLine(to: CGPoint(x: 32, y: h)) 36 | p.addCurve(to: CGPoint(x: 16, y: h - 16), 37 | control1: CGPoint(x: 24, y: h), 38 | control2: CGPoint(x: 16, y: h - 8)) 39 | 40 | p.addLine(to: CGPoint(x: 16, y: h)) 41 | } 42 | 43 | return path 44 | } 45 | 46 | func leftBubble(in rect: CGRect) -> Path { 47 | let w = rect.width 48 | let h = rect.height 49 | 50 | let path = Path { p in 51 | // Flip the x-coordinates of each point by subtracting them from the width 52 | p.move(to: CGPoint(x: w - 16, y: 16)) 53 | p.addCurve(to: CGPoint(x: w - 32, y: 0), 54 | control1: CGPoint(x: w - 16, y: 8), 55 | control2: CGPoint(x: w - 24, y: 0)) 56 | p.addLine(to: CGPoint(x: 8, y: 0)) 57 | p.addLine(to: CGPoint(x: 16, y: 8)) 58 | p.addLine(to: CGPoint(x: 16, y: h - 16)) 59 | 60 | p.addCurve(to: CGPoint(x: 32, y: h), 61 | control1: CGPoint(x: 16, y: h - 8), 62 | control2: CGPoint(x: 24, y: h)) 63 | 64 | p.addLine(to: CGPoint(x: w - 32, y: h)) 65 | p.addCurve(to: CGPoint(x: w - 16, y: h - 16), 66 | control1: CGPoint(x: w - 24, y: h), 67 | control2: CGPoint(x: w - 16, y: h - 8)) 68 | 69 | p.addLine(to: CGPoint(x: w - 16, y: h)) 70 | } 71 | 72 | return path 73 | } 74 | 75 | } 76 | 77 | 78 | -------------------------------------------------------------------------------- /OllamaGUI/UI/ChatBubble.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChatBubble.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/10/24. 6 | // 7 | 8 | import MarkdownUI 9 | import SwiftUI 10 | 11 | struct ChatBubble: View { 12 | var chat: ChatModel 13 | 14 | var body: some View { 15 | Group{ 16 | if chat.isSystem { 17 | EmptyView() 18 | } 19 | else { 20 | HStack { 21 | if chat.isMe { 22 | Spacer() 23 | } 24 | content.clipShape(BubbleShape(role: chat.message?.role ?? .user)) 25 | if !chat.isMe { 26 | Spacer() 27 | } 28 | }.frame(maxWidth: .infinity) 29 | } 30 | } 31 | } 32 | 33 | @ViewBuilder 34 | var content: some View { 35 | let message = (chat.message?.content ?? "").removeFirstBreakLine 36 | Markdown { 37 | message 38 | } 39 | .markdownTheme((chat.isMe) ? .user : .assistant) 40 | .padding(.horizontal, 30) 41 | .padding(.vertical, 16) 42 | .textSelection(.enabled) 43 | .foregroundColor((chat.isMe) ? .white : .black) 44 | .background((chat.isMe) ? .blue : .white) 45 | 46 | 47 | // Text(.init(message)) 48 | // .padding(.horizontal, 30) 49 | // .padding(.vertical, 16) 50 | // .background((chat.isMe) ? .blue : .white) 51 | // .foregroundColor((chat.isMe) ? .white : .black) 52 | } 53 | 54 | } 55 | 56 | -------------------------------------------------------------------------------- /OllamaGUI/UI/ChatHeader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChatHeader.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/15/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ChatHeader: View { 11 | @Environment(\.injected) var container 12 | var room:RoomEntity 13 | @Binding var show:Bool 14 | var onClean: ()->Void 15 | var body: some View { 16 | HStack(spacing: 0){ 17 | Image(systemName: "message.fill").padding(.trailing) 18 | Text((room.title ?? "untitled").removeFirstBreakLine).lineLimit(1) 19 | Spacer() 20 | Button(action: { 21 | show.toggle() 22 | }, label: { 23 | Image(systemName: "xmark") 24 | }).buttonStyle(SidebarButton()) 25 | }.padding(.leading).padding(.vertical) 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /OllamaGUI/UI/ChatViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChatViewModel.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/13/24. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | import Combine 11 | 12 | class ChatViewModel: ObservableObject { 13 | @Published var state: LangchainState = .initState 14 | @Published var bloc: LangchainBloc! 15 | 16 | func ignite(container: DIContainer, room: RoomEntity) { 17 | self.bloc = LangchainBloc( 18 | langchainUsecase: container.langchainusecase, 19 | chatUsecase: container.chatusecase, 20 | appSetting: container.appSetting, 21 | roomOption: room.option 22 | ) 23 | print("bloc has been created") 24 | bloc.stateSubject.assign(to: &$state) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /OllamaGUI/UI/Commen/ClearButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ClearButton.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/15/24. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct ClearButton: ButtonStyle { 12 | private var enabled: Bool = true 13 | 14 | init(enabled: Bool = true) { 15 | self.enabled = enabled 16 | } 17 | 18 | func makeBody(configuration: Configuration) -> some View { 19 | configuration.label 20 | .opacity(configuration.isPressed ? 0.8 : 1) 21 | .padding(.vertical, 8) 22 | .padding(.horizontal, 15) 23 | .frame(minWidth: 48, minHeight: 0) 24 | .foregroundColor(.white) 25 | .focusEffectDisabled() 26 | .background(.clear) 27 | .clipShape(RoundedRectangle(cornerRadius: 3)) 28 | } 29 | } 30 | 31 | #Preview { 32 | VStack { 33 | Button(action: {}) { 34 | Text("전송") 35 | }.buttonStyle(ClearButton()) 36 | Button(action: {}) { 37 | Text("전송") 38 | }.buttonStyle(ClearButton(enabled: false)) 39 | 40 | }.frame(width: 500, height: 500) 41 | } 42 | -------------------------------------------------------------------------------- /OllamaGUI/UI/Commen/CommonButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommonButton.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/11/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CommonButton: ButtonStyle { 11 | private var enabled: Bool = true 12 | 13 | init(enabled: Bool = true) { 14 | self.enabled = enabled 15 | } 16 | 17 | func makeBody(configuration: Configuration) -> some View { 18 | configuration.label 19 | .opacity(configuration.isPressed ? 0.8 : 1) 20 | .padding(.vertical, 8) 21 | .padding(.horizontal, 15) 22 | .frame(minWidth: 48, minHeight: 0) 23 | .foregroundColor(.white) 24 | .focusEffectDisabled() 25 | .background(enabled ? .blue: .gray) 26 | .clipShape(RoundedRectangle(cornerRadius: 3)) 27 | } 28 | } 29 | 30 | #Preview { 31 | VStack { 32 | Button(action: {}) { 33 | Text("전송") 34 | }.buttonStyle(CommonButton()) 35 | Button(action: {}) { 36 | Text("전송") 37 | }.buttonStyle(CommonButton(enabled: false)) 38 | 39 | }.frame(width: 500, height: 500) 40 | } 41 | -------------------------------------------------------------------------------- /OllamaGUI/UI/Commen/Controller/KeyPressedController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyPressedController.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/15/24. 6 | // 7 | 8 | import Foundation 9 | import Cocoa 10 | 11 | class KeyPressedController: ObservableObject { 12 | @Published var isShiftPressed = false 13 | 14 | init() { 15 | NSEvent 16 | .addLocalMonitorForEvents(matching: .flagsChanged) { [weak self] event -> NSEvent? in 17 | if event.modifierFlags.contains(.shift) { 18 | self?.isShiftPressed = true 19 | } else { 20 | self?.isShiftPressed = false 21 | } 22 | return event 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /OllamaGUI/UI/Commen/EditorButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommonButton.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/11/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct EditorButton: ButtonStyle { 11 | @Binding var enabled: Bool 12 | @State private var color:Color = .gray 13 | 14 | 15 | 16 | func makeBody(configuration: Configuration) -> some View { 17 | configuration.label 18 | .frame(width: 20,height: 20) 19 | .opacity(configuration.isPressed ? 0.8 : 1) 20 | .frame(minWidth: 40, minHeight: 40,alignment: .center) 21 | .foregroundColor(enabled ? .blue : color) 22 | .background(.clear) 23 | .clipShape(RoundedRectangle(cornerRadius: 3)) 24 | .onHover(perform: { hovering in 25 | if enabled { 26 | return 27 | } 28 | if hovering { 29 | color = .blue 30 | }else{ 31 | color = .gray 32 | } 33 | 34 | }) 35 | } 36 | } 37 | 38 | #Preview { 39 | VStack { 40 | Button(action: {}) { 41 | Image("MicIcon") 42 | }.buttonStyle(EditorButton(enabled: .constant(true))) 43 | Button(action: {}) { 44 | Image("MicIcon") 45 | }.buttonStyle(EditorButton(enabled: .constant(true))) 46 | 47 | }.frame(width: 500, height: 500) 48 | } 49 | -------------------------------------------------------------------------------- /OllamaGUI/UI/Commen/SidebarButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SidebarButton.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/9/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct SidebarButton: ButtonStyle { 11 | func makeBody(configuration: Configuration) -> some View { 12 | configuration.label 13 | .frame(width: 25,height: 25) 14 | .frame(width: 40, height: 40,alignment: .center) 15 | .foregroundColor(.sidebarIcon) 16 | .opacity(configuration.isPressed ? 0.8 : 1) 17 | .focusEffectDisabled() 18 | } 19 | 20 | } 21 | 22 | #Preview { 23 | VStack { 24 | Button(action: {}) { 25 | Image(systemName: "message") 26 | }.buttonStyle(SidebarButton()) 27 | }.frame(width: 500,height: 500) 28 | } 29 | -------------------------------------------------------------------------------- /OllamaGUI/UI/Commen/XProgressView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProgressView.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/22/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct XProgressView: View { 11 | @Binding var progress: Float? 12 | var text: String 13 | 14 | @Binding var present: Bool 15 | var body: some View { 16 | VStack { 17 | Text(text).font(.body).padding() 18 | if progress != nil { 19 | ProgressView(value: progress) 20 | .frame(maxWidth: .infinity, minHeight: 20, maxHeight: 20) 21 | .padding(.bottom).padding(.horizontal) 22 | } else { 23 | ProgressView() 24 | .controlSize(.small).padding(.bottom) 25 | } 26 | }.frame(width: 300, height: 100) 27 | } 28 | } 29 | 30 | #Preview { 31 | XProgressView( 32 | progress: .constant(nil), 33 | text: "this is Text", 34 | present: .constant(true) 35 | ) 36 | } 37 | 38 | #Preview { 39 | XProgressView( 40 | progress: .constant(0.5), 41 | text: "this is Text", 42 | present: .constant(true) 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /OllamaGUI/UI/Home/HomeView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeView.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/9/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct HomeView: View { 11 | var body: some View { 12 | Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/).frame(maxWidth: .infinity,maxHeight: .infinity) 13 | } 14 | } 15 | 16 | #Preview { 17 | HomeView() 18 | } 19 | -------------------------------------------------------------------------------- /OllamaGUI/UI/Manager/Components/LibraryModelListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LibraryModelList.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/20/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct LibraryModelListView: View { 11 | var model: OllamaModel 12 | @State var showTags = false 13 | @Binding var installed:[ModelInfoModel] 14 | 15 | var onUpdate: ()->Void 16 | 17 | var body: some View { 18 | VStack(spacing: 0) { 19 | HStack { 20 | Text(model.title) 21 | Spacer() 22 | Text(model.updatedAt) 23 | } 24 | Text(model.desc).textLeft.font(.body).foregroundColor(.gray) 25 | Spacer() 26 | HStack { 27 | Text(model.pulls) 28 | Text(model.tagsCount) 29 | Spacer() 30 | Button("show tags") { 31 | showTags.toggle() 32 | } 33 | } 34 | 35 | }.padding(.vertical).padding(.horizontal,0).frame(height: 80) 36 | .sheet(isPresented: $showTags, content: { 37 | TagsView(showTags:$showTags, model: model, installed:installed,onUpdate: onUpdate) 38 | }) 39 | } 40 | } 41 | 42 | //#Preview { 43 | // List{ 44 | // LibraryModelListView(model: OllamaModel(),installed:.constant([])) 45 | // }.listStyle(.plain) 46 | //} 47 | -------------------------------------------------------------------------------- /OllamaGUI/UI/Manager/Components/ModelListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModelListView.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/20/24. 6 | // 7 | 8 | import Combine 9 | import SwiftUI 10 | 11 | struct ModelListView: View { 12 | @Environment(\.injected) var container 13 | var model: ModelInfoModel 14 | 15 | @State var showProgress: Bool = false 16 | @State var showAlert: Bool = false 17 | @State var cancel = Set() 18 | @State var subject = PassthroughSubject() 19 | 20 | var fetchModel: () -> Void 21 | 22 | var body: some View { 23 | VStack(spacing: 0) { 24 | HStack { 25 | Text(model.name) 26 | Spacer() 27 | Text(model.modifiedAt, style: .date) 28 | } 29 | Spacer() 30 | HStack { 31 | Spacer() 32 | Text(model.size.getSize) 33 | Button("remove") { 34 | handleClick() 35 | } 36 | } 37 | 38 | }.padding(.vertical).padding(.horizontal, 0).frame(height: 80) 39 | .onReceive(subject, perform: handleReceive) 40 | .sheet(isPresented: $showProgress, content: { 41 | XProgressView( 42 | progress: .constant(nil), 43 | text: "delete model \(model.name)", 44 | present: $showProgress 45 | ) 46 | }).alert(isPresented: $showAlert, content: { 47 | Alert( 48 | title: Text("error"), 49 | message: Text("some error occurred when delete model"), 50 | dismissButton: .default(Text("ok")) 51 | ) 52 | }) 53 | } 54 | } 55 | 56 | extension ModelListView { 57 | func handleClick() { 58 | subject = container.interactor.deleteModel( 59 | cancel: &cancel, 60 | model: model, 61 | setting: container.appSetting 62 | ) 63 | } 64 | 65 | func handleReceive(_ value: Bool) { 66 | showProgress = value 67 | if showProgress { 68 | fetchModel() 69 | } else { 70 | showAlert.toggle() 71 | } 72 | } 73 | } 74 | 75 | #Preview { 76 | List { 77 | // ModelListView(model: .preview) 78 | // ModelListView(model: .preview) 79 | // ModelListView(model: .preview) 80 | // ModelListView(model: .preview) 81 | // ModelListView(model: .preview) 82 | // ModelListView(model: .preview) 83 | }.listStyle(.plain) 84 | } 85 | -------------------------------------------------------------------------------- /OllamaGUI/UI/OverlayToast.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OverlayToast.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/18/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct OverlayToast: View { 11 | var text: String = "preview" 12 | 13 | @State private var width: Double = 0 14 | @State private var opacity: Double = 0 15 | @State private var offsetY: Double = 20 16 | 17 | var body: some View { 18 | GeometryReader { geo in 19 | 20 | Text(text) 21 | .font(.body) 22 | .lineLimit(1) 23 | .padding(.horizontal) 24 | .padding(.vertical, 8) 25 | .background(.gray) 26 | .clipShape(RoundedRectangle(cornerRadius: 20)) 27 | .frame(width: width) 28 | .position(CGPoint(x: geo.size.width / 2, 29 | y: geo.size.height - 170)) 30 | .offset(y: offsetY) 31 | .opacity(opacity) 32 | .onAppear { 33 | withAnimation(.snappy) { 34 | width = Double(text.count * 14 + 16) 35 | opacity = 1 36 | offsetY = 0 37 | } 38 | 39 | DispatchQueue.global() 40 | .asyncAfter(deadline: .now() + 3) { [self] in 41 | DispatchQueue.main.async { [self] in 42 | withAnimation(.snappy) { 43 | width = 0 44 | opacity = 0 45 | offsetY = 20 46 | } 47 | } 48 | } 49 | } 50 | } 51 | } 52 | } 53 | 54 | #Preview { 55 | ChatView( 56 | show: .constant(true), 57 | position: .constant(.zero), 58 | floating: .constant(true), 59 | room: .randomRoom 60 | ) 61 | } 62 | -------------------------------------------------------------------------------- /OllamaGUI/UI/Room/RoomView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RoomView.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/17/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct RoomView: View { 11 | @Binding var rooms: [RoomEntity] 12 | var onInsert: () -> Void 13 | var onDelete: (RoomEntity) -> Void 14 | 15 | var body: some View { 16 | VStack { 17 | HStack { 18 | Text("Chat").font(.title) 19 | Spacer() 20 | Button(action: onInsert) { 21 | Image(systemName: "plus").resizable() 22 | .frame(width: 16, height: 16) 23 | }.buttonStyle(SidebarButton()) 24 | } 25 | .padding(.top, 16) 26 | .padding(.leading) 27 | .padding(.bottom, 8) 28 | ScrollView { 29 | LazyVStack { 30 | ForEach(rooms, id: \.id) { room in 31 | RoomItem(room: room, onDelete: onDelete) 32 | } 33 | } 34 | }.frame(maxWidth: .infinity) 35 | }.ignoresSafeArea().background(.chat) 36 | } 37 | } 38 | 39 | #Preview { 40 | RootView().injectPreview() 41 | } 42 | -------------------------------------------------------------------------------- /OllamaGUI/UI/SettingSheetViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingSheetViewModel.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/5/24. 6 | // 7 | 8 | import Foundation 9 | 10 | class SettingSheetViewModel: ObservableObject { 11 | @Published var options: RoomOptionEntity? 12 | @Published var system: String 13 | @Published var topk: Float 14 | @Published var topp: Float 15 | @Published var temperature: Float 16 | 17 | 18 | 19 | init() { 20 | options = nil 21 | system = "" 22 | topk = 40 23 | topp = 0.9 24 | temperature = 0.8 25 | } 26 | 27 | 28 | func reset() { 29 | options = nil 30 | system = "" 31 | topk = 40 32 | topp = 0.9 33 | temperature = 0.8 34 | } 35 | 36 | 37 | func apply() { 38 | if options == nil { 39 | options = RoomOptionEntity(model: nil, system: nil) 40 | } 41 | guard let options = options else { 42 | fatalError() 43 | } 44 | options.top_k = Int(topk) 45 | options.top_p = topp 46 | options.temperature = temperature 47 | if !system.isEmpty { 48 | options.system = system 49 | } 50 | print("success") 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /OllamaGUI/UI/SideBar/SideBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SideBar.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/9/24. 6 | // 7 | 8 | import Combine 9 | import SwiftUI 10 | 11 | struct SideBar: View { 12 | @Environment(\.injected) var container: DIContainer 13 | @Binding var rooms: [RoomEntity] 14 | @Binding var selected: RoomEntity? 15 | 16 | @State private var cancel = Set() 17 | @State private var subject = CurrentValueSubject(false) 18 | @State private var status: Bool = false 19 | @State private var showSetting: Bool = false 20 | var onSelect: (RoomEntity) -> Void 21 | var onInsert: () -> Void 22 | var onDelete: (RoomEntity) -> Void 23 | 24 | var body: some View { 25 | VStack { 26 | Spacer().frame(height: 35) 27 | Button(action: {}) { 28 | Image(systemName: "message").resizable() 29 | }.buttonStyle(SidebarButton()) 30 | Spacer() 31 | Group { 32 | if status { 33 | Color.green 34 | } else { 35 | Color.red 36 | } 37 | }.frame(width: 15, height: 15) 38 | .clipShape(Circle()) 39 | .shadow(radius: 5) 40 | Button(action: { 41 | showSetting.toggle() 42 | }) { 43 | Image(systemName: "gear").resizable() 44 | }.buttonStyle(SidebarButton()) 45 | Spacer().frame(height: 35) 46 | 47 | }.onAppear { 48 | subject = container.interactor.checkNetwork( 49 | cancel: &cancel, 50 | setting: container.appSetting, 51 | baseUrl: nil, 52 | isTest: false 53 | ) 54 | }.onReceive(subject, perform: { status in 55 | self.status = status 56 | }) 57 | .sheet(isPresented: $showSetting, content: { 58 | SettingView(showSetting: $showSetting) 59 | }) 60 | } 61 | } 62 | 63 | #Preview { 64 | RootView().injectPreview() 65 | } 66 | -------------------------------------------------------------------------------- /OllamaGUI/UI/StopButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StopButton.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/15/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct StopButton: View { 11 | 12 | var onCancel:()->Void 13 | var body: some View { 14 | Button(action: {onCancel()}, label: { 15 | Label("STOP",systemImage: "stop.fill") 16 | }).buttonStyle(CommonButton()) 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /OllamaGUI/UI/Tags/TagListItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TagListItem.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/22/24. 6 | // 7 | 8 | import Combine 9 | import Foundation 10 | import SwiftUI 11 | 12 | struct TagListItem: View { 13 | @Environment(\.injected) var container 14 | 15 | @State private var showProgress: Bool = false 16 | @State private var progress: Float? 17 | @State private var response: PullResponseModel? 18 | @State private var subject = PassthroughSubject< 19 | Loadable, 20 | Never 21 | >() 22 | @State private var bag = Set() 23 | 24 | var tag: OllamaTagModel 25 | var onPull: () -> Void 26 | 27 | var body: some View { 28 | VStack(spacing: 0) { 29 | HStack { 30 | Text(tag.title) 31 | Spacer() 32 | Text(tag.updatedAt) 33 | } 34 | Text(tag.sha).textLeft.font(.caption).foregroundColor(.gray) 35 | HStack { 36 | Text(tag.size).font(.caption).foregroundColor(.gray) 37 | Spacer() 38 | Button("pull") { 39 | handlePull() 40 | }.disabled(showProgress) 41 | } 42 | 43 | }.padding(.vertical).padding(.horizontal, 0).frame(height: 80) 44 | .sheet( 45 | isPresented: $showProgress, 46 | content: { 47 | XProgressView( 48 | progress: $progress, 49 | text: response?.status ?? "initialize", 50 | present: $showProgress 51 | ) 52 | } 53 | ).onReceive( 54 | subject, 55 | perform: handleReceive 56 | ) 57 | } 58 | } 59 | 60 | extension TagListItem { 61 | func handleReceive(value: Loadable) { 62 | switch value { 63 | case let .isLoading(last: val): 64 | response = val 65 | progress = val?.progress 66 | return 67 | case let .loaded(val): 68 | response = val 69 | progress = val.progress 70 | showProgress = false 71 | case .finished: 72 | showProgress = false 73 | onPull() 74 | 75 | default: 76 | return 77 | } 78 | } 79 | 80 | func handlePull() { 81 | subject = container.interactor.pullModel( 82 | cancel: &bag, 83 | model: tag, 84 | setting: container.appSetting 85 | ) 86 | showProgress.toggle() 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /OllamaGUI/V2/Core.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Core.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/8/24. 6 | // 7 | 8 | import Foundation 9 | -------------------------------------------------------------------------------- /OllamaGUI/V2/Core/SessionManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SessionManager.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/8/24. 6 | // 7 | 8 | import Foundation 9 | 10 | class SessionManager { 11 | var session: URLSession 12 | 13 | init() { 14 | session = URLSession(configuration: .default) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /OllamaGUI/V2/Data/Datasource/OllamaDatasource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OllamaDatasource.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/8/24. 6 | // 7 | 8 | import Combine 9 | import Foundation 10 | 11 | protocol OllamaDatasource { 12 | var baseUrl: String { get set } 13 | var session: URLSession { get } 14 | func getEmbedding(prompt: String, model: String) 15 | -> AnyPublisher<[Float], NetworkError> 16 | 17 | func chat(req: ChatRequestModel) -> AnyPublisher 18 | } 19 | 20 | class OlamaDatasourceStub: OllamaDatasource { 21 | var baseUrl: String 22 | let session: URLSession 23 | 24 | init(baseUrl: String, session: URLSession) { 25 | self.baseUrl = baseUrl 26 | self.session = session 27 | } 28 | 29 | func getEmbedding(prompt _: String, 30 | model _: String) -> AnyPublisher<[Float], NetworkError> 31 | { 32 | return Just([]).setFailureType(to: NetworkError.self) 33 | .eraseToAnyPublisher() 34 | } 35 | 36 | 37 | func chat(req: ChatRequestModel) -> AnyPublisher { 38 | return Just(.init(entity: ChatEntity(message: .randomMessage, createdAt: .now))).setFailureType(to: NetworkError.self) 39 | .eraseToAnyPublisher() 40 | } 41 | } 42 | 43 | class OllamaDatasourceImpl: OllamaDatasource { 44 | var baseUrl: String 45 | let session: URLSession 46 | 47 | init(baseUrl: String, session: URLSession) { 48 | self.baseUrl = baseUrl 49 | self.session = session 50 | } 51 | 52 | 53 | func getEmbedding(prompt: String, 54 | model: String) -> AnyPublisher<[Float], NetworkError> 55 | { 56 | let api = APICall( 57 | session: session, 58 | baseUrl: baseUrl, 59 | url: "/api/embeddings", 60 | method: .post 61 | ) 62 | 63 | let dto = EmbeddingDto(model: model, prompt: prompt) 64 | 65 | return api.call(data: dto).map { resDto in 66 | resDto.embedding 67 | }.eraseToAnyPublisher() 68 | } 69 | 70 | 71 | func chat(req: ChatRequestModel) -> AnyPublisher { 72 | var api = APICall( 73 | session: session, baseUrl: baseUrl, url: "/api/chat", method: .post) 74 | 75 | return api.callStream(data: req).eraseToAnyPublisher() 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /OllamaGUI/V2/Data/Datasource/WebDatasource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WebDatasource.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/8/24. 6 | // 7 | 8 | import Combine 9 | import Foundation 10 | 11 | protocol WebDatasource { 12 | func crawlingWeb(for url: String) -> AnyPublisher 13 | } 14 | 15 | class WebDatasourceImpl: WebDatasource { 16 | var session: URLSession 17 | 18 | init(session: URLSession) { 19 | self.session = session 20 | } 21 | 22 | func crawlingWeb(for url: String) -> AnyPublisher { 23 | let req = APICall(session: session, baseUrl: url, url: "", 24 | method: .get) 25 | return req.callWithoutDecode().eraseToAnyPublisher() 26 | } 27 | } 28 | 29 | class WebDatasourceStub: WebDatasource { 30 | func crawlingWeb(for _: String) -> AnyPublisher { 31 | return Just("").setFailureType(to: NetworkError.self) 32 | .eraseToAnyPublisher() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /OllamaGUI/V2/Data/Dto/EmbeddingDto.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmbeddingDto.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/8/24. 6 | // 7 | 8 | import Foundation 9 | 10 | class EmbeddingDto: Encodable { 11 | var model: String 12 | var prompt: String 13 | 14 | init(model: String, prompt: String) { 15 | self.model = model 16 | self.prompt = prompt 17 | } 18 | 19 | enum CodingKeys: CodingKey { 20 | case model 21 | case prompt 22 | } 23 | 24 | func encode(to encoder: any Encoder) throws { 25 | var container = encoder.container(keyedBy: CodingKeys.self) 26 | try container.encode(model, forKey: .model) 27 | try container.encode(prompt, forKey: .prompt) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /OllamaGUI/V2/Data/Dto/EmbeddingResponseDto.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmbeddingResponseDto.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/8/24. 6 | // 7 | 8 | import Foundation 9 | 10 | class EmbeddingResponseDto: Decodable { 11 | var embedding: [Float] 12 | 13 | enum CodingKeys: CodingKey { 14 | case embedding 15 | } 16 | 17 | required init(from decoder: any Decoder) throws { 18 | let container = try decoder.container(keyedBy: CodingKeys.self) 19 | embedding = try container.decode([Float].self, forKey: .embedding) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /OllamaGUI/V2/Data/Repository/CrawlingRepositoryImpl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CrawlingRepositoryImpl.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/8/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | class CrawlingRepositoryImpl: CrawlingRepository { 12 | var datasource: WebDatasource 13 | 14 | init(datasource: WebDatasource) { 15 | self.datasource = datasource 16 | } 17 | 18 | func crawlingWeb(for url: String) -> AnyPublisher { 19 | datasource.crawlingWeb(for: url) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /OllamaGUI/V2/Data/Repository/OllmaRepositoryImpl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmbedingRepositoryImpl.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/8/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | class OllamaRepositoryImpl: OllamaRepository { 12 | var dataSource: OllamaDatasource 13 | 14 | init(dataSource: OllamaDatasource) { 15 | self.dataSource = dataSource 16 | } 17 | 18 | func getEmbeding(prompt: String, 19 | model: String) -> AnyPublisher<[Float], NetworkError> 20 | { 21 | dataSource.getEmbedding(prompt: prompt, model: model) 22 | } 23 | 24 | func chat(req: ChatRequestModel) -> AnyPublisher { 25 | dataSource.chat(req: req) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /OllamaGUI/V2/Domain/Repository/CrawlingRepository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CrawlingRepository.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/8/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | 12 | protocol CrawlingRepository{ 13 | func crawlingWeb(for: String) -> AnyPublisher 14 | } 15 | -------------------------------------------------------------------------------- /OllamaGUI/V2/Domain/Repository/OllamaRepository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmbedingRepository.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/8/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | protocol OllamaRepository { 12 | func getEmbeding(prompt: String,model:String)->AnyPublisher<[Float],NetworkError> 13 | func chat(req:ChatRequestModel)->AnyPublisher 14 | } 15 | -------------------------------------------------------------------------------- /OllamaGUI/V2/Domain/Usecase/ChatUsecase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChatUsecase.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/11/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | class ChatUsecase { 12 | var ollamaRepository: OllamaRepository 13 | var bag = Set() 14 | 15 | init(ollamaRepository: OllamaRepository) { 16 | self.ollamaRepository = ollamaRepository 17 | } 18 | 19 | func cancelChat() { 20 | bag.removeAll() 21 | } 22 | 23 | func chatV2(req: ChatRequestModel, appSetting: AppSetting,option: RoomOptionEntity?) -> AnyPublisher { 24 | var dto = req 25 | dto.model = appSetting.model 26 | dto = dto.applyOption(option: option) 27 | return ollamaRepository.chat(req: dto).eraseToAnyPublisher() 28 | } 29 | 30 | func chat(req: ChatRequestModel, appSetting:AppSetting, options:RoomOptionEntity?) -> CurrentValueSubject,Never>{ 31 | var dto = req 32 | dto.model = appSetting.model 33 | dto = dto.applyOption(option: options) 34 | let subject = CurrentValueSubject,Never>(.initState) 35 | 36 | 37 | ollamaRepository.chat(req: dto) 38 | .sink(receiveCompletion: { [weak self] com in 39 | switch com { 40 | case .finished: 41 | subject.send(.finished) 42 | subject.send(completion: .finished) 43 | self?.bag.removeAll() 44 | case let .failure(error): 45 | subject.send(.failed(error)) 46 | self?.bag.removeAll() 47 | } 48 | }, receiveValue: { val in 49 | subject.send(.isLoading(last: val)) 50 | 51 | }) 52 | .store(in: &bag) 53 | return subject 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /OllamaGUI/V2/UI_v2/Chat/Bloc/LangchainEvent+State.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LangchainEvent+State.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/12/24. 6 | // 7 | 8 | import Foundation 9 | 10 | enum LangchainEvent { 11 | case GET_DOCUMENT_WEB(url: String) 12 | case REGENERATE_DOCUMENT(url: String) 13 | case GENERATE_EMBEDDING(prompt: String) 14 | case GET_ANSWER 15 | } 16 | 17 | enum LangchainState : BaseState{ 18 | case initState 19 | case getDocument(url: String) 20 | case getEmbedding(progress: Int, total: Int) 21 | case idle(embeding: Embeddings,util: USearchUtil) 22 | case generating 23 | case readyToAsk(embedding: Embeddings, util: USearchUtil, context: [Document], prompt: String) 24 | case answer(embedding: Embeddings, util: USearchUtil, answer: Loadable) 25 | case generateAnswer(answer: String) 26 | case error(error: MuseError) 27 | } 28 | 29 | -------------------------------------------------------------------------------- /OllamaGUI/V2/UI_v2/Root/RootView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RootView.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 2/18/24. 6 | // 7 | 8 | import SwiftUI 9 | import SwiftUIIntrospect 10 | 11 | struct RootView: View { 12 | @Environment(\.modelContext) private var context 13 | @Environment(\.injected) private var container 14 | 15 | @ObservedObject private var viewModel = RootViewModel() 16 | 17 | var body: some View { 18 | Group { 19 | if viewModel.window != nil && viewModel.settingLoaded { 20 | root.onAppear { 21 | viewModel.afterWindowSet() 22 | } 23 | } else { 24 | EmptyView() 25 | } 26 | } 27 | .onAppear { 28 | viewModel.setup(container, context) 29 | viewModel.beforeWindowSet() 30 | }.background { 31 | WindowAccessor(window: $viewModel.window) 32 | } 33 | } 34 | } 35 | 36 | extension RootView { 37 | @ViewBuilder 38 | private var root: some View { 39 | NavigationSplitView( 40 | columnVisibility: $viewModel.sideBar, sidebar: { 41 | sidebar 42 | }, 43 | detail: { 44 | room 45 | } 46 | ) 47 | .navigationSplitViewStyle(.balanced) 48 | .background(.chatHover) 49 | .introspect( 50 | .navigationSplitView, 51 | on: .macOS(.v14, .v13), 52 | customize: { splitView in 53 | (splitView.delegate as? NSSplitViewController)? 54 | .splitViewItems 55 | .first?.canCollapse = false 56 | } 57 | ) 58 | } 59 | 60 | @ViewBuilder 61 | private var room: some View { 62 | RoomView( 63 | rooms: $viewModel.rooms, 64 | onInsert: viewModel.onInsert, 65 | onDelete: viewModel.onDelete 66 | ) 67 | } 68 | 69 | @ViewBuilder 70 | private var sidebar: some View { 71 | ZStack { 72 | Color.sidebar.ignoresSafeArea() 73 | SideBar( 74 | rooms: $viewModel.rooms, 75 | selected: $viewModel.room, 76 | onSelect: viewModel.onSelect, 77 | onInsert: viewModel.onInsert, 78 | onDelete: viewModel.onDelete 79 | ) 80 | }.toolbar(removing: .sidebarToggle) 81 | .navigationSplitViewColumnWidth( 82 | min: 65, 83 | ideal: 65, 84 | max: 65 85 | ) 86 | } 87 | } 88 | 89 | 90 | #Preview { 91 | RootView() 92 | } 93 | -------------------------------------------------------------------------------- /OllamaGUI/V2/UI_v2/SidebarV2/SidebarV2.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SidebarV2.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 2/18/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct SidebarV2: View { 11 | var body: some View { 12 | Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) 13 | } 14 | } 15 | 16 | #Preview { 17 | SidebarV2() 18 | } 19 | -------------------------------------------------------------------------------- /OllamaGUI/V2/UI_v2/SidebarV2/SidebarV2ViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SidebarV2ViewModel.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 2/18/24. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | -------------------------------------------------------------------------------- /OllamaGUI/V2/UI_v2/model/Embeddings.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Embeddings.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/12/24. 6 | // 7 | 8 | import Foundation 9 | 10 | enum EmbeddingsType { 11 | case web 12 | } 13 | 14 | protocol Embeddings { 15 | var type: EmbeddingsType { get } 16 | var doc: [Document] { get set } 17 | var vector: [[Float]] { get set } 18 | var name: String { get } 19 | } 20 | 21 | struct WebEmbeddings: Embeddings { 22 | var type: EmbeddingsType 23 | var doc: [Document] 24 | var html: String 25 | var url: String 26 | var vector: [[Float]] 27 | 28 | init(type: EmbeddingsType, doc: [Document], url: String) { 29 | self.type = type 30 | self.doc = doc 31 | self.url = url 32 | vector = [] 33 | html = "" 34 | } 35 | 36 | var name: String { 37 | url.base64Data!.base64EncodedString() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /OllamaGUI/ollamaGUI.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | com.apple.security.network.client 10 | 11 | com.apple.security.network.server 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /OllamaGUI/ollamaGUIApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ollamaGUIApp.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 1/9/24. 6 | // 7 | 8 | import SwiftData 9 | import SwiftUI 10 | 11 | struct VisualEffect: NSViewRepresentable { 12 | func makeNSView(context _: Self.Context) -> NSView { 13 | let view = NSVisualEffectView() 14 | view.blendingMode = .behindWindow 15 | view.state = .active 16 | view.material = .underWindowBackground 17 | return view 18 | } 19 | 20 | func updateNSView(_: NSView, context _: Context) {} 21 | } 22 | 23 | @main 24 | struct ollamaGUIApp: App { 25 | var body: some Scene { 26 | WindowGroup { 27 | RootView() 28 | .frame(minWidth: 390,maxWidth: 390, minHeight: 640,idealHeight:640) 29 | .inject() 30 | .preferredColorScheme(.dark) 31 | .modelContainer( 32 | for: [MessageEntity.self, ChatEntity.self, RoomEntity.self,AppSettingEntity.self] 33 | ) 34 | } 35 | .windowStyle(HiddenTitleBarWindowStyle()) 36 | .windowResizability(.contentSize) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /OllamaGuiTest/Data/Datasource/OllamaDatasource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OllamaDatasource.swift 3 | // ollamaGuiTest 4 | // 5 | // Created by 배상휘 on 3/8/24. 6 | // 7 | 8 | @testable import ollamaGUI 9 | import XCTest 10 | 11 | final class OllamaDatasourceTest: XCTestCase { 12 | var manager: SessionManager! 13 | var datasource: OllamaDatasource! 14 | 15 | override func setUp() { 16 | manager = SessionManager() 17 | datasource = OllamaDatasourceImpl( 18 | baseUrl: "http://localhost:11434", 19 | session: manager.session 20 | ) 21 | } 22 | 23 | override func tearDown() { 24 | manager = nil 25 | datasource = nil 26 | } 27 | 28 | func test_임베딩() { 29 | let expectation = expectation(description: "get embeded") 30 | var res:[Float] = [] 31 | let cancel = datasource.getEmbedding(prompt: "hello hi", model: "mistral") 32 | .sink(receiveCompletion: { 33 | switch $0 { 34 | case .finished: 35 | expectation.fulfill() 36 | XCTAssert(true,"success with \(res)") 37 | case let .failure(error): 38 | XCTAssert(false, "error with \(error.localizedDescription)") 39 | } 40 | }, receiveValue: { value in 41 | res = value 42 | 43 | }) 44 | 45 | waitForExpectations(timeout: 20) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /OllamaGuiTest/Data/Datasource/WebDatasource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WebDatasource.swift 3 | // ollamaGuiTest 4 | // 5 | // Created by 배상휘 on 3/8/24. 6 | // 7 | 8 | @testable import ollamaGUI 9 | import XCTest 10 | 11 | final class WebDatasourceTest: XCTestCase { 12 | var manager: SessionManager! 13 | var datasource: WebDatasource! 14 | 15 | override func setUp() { 16 | manager = SessionManager() 17 | datasource = WebDatasourceImpl( 18 | session: manager.session 19 | ) 20 | } 21 | 22 | override func tearDown() { 23 | manager = nil 24 | datasource = nil 25 | } 26 | 27 | func test_크롤링테스트() { 28 | let expectation = expectation(description: "crawling google") 29 | var res = "" 30 | _ = datasource.crawlingWeb(for: "https://google.com") 31 | .sink(receiveCompletion: { 32 | switch $0 { 33 | case .finished: 34 | expectation.fulfill() 35 | print(res) 36 | XCTAssert(true, "success with \(res)") 37 | case let .failure(error): 38 | XCTAssert(false, "error with \(error.localizedDescription)") 39 | expectation.fulfill() 40 | } 41 | }, receiveValue: { value in 42 | res = value 43 | 44 | }) 45 | 46 | waitForExpectations(timeout: 20) 47 | } 48 | 49 | func test_크롤링테스트2() { 50 | let expectation = expectation(description: "crawling wiki") 51 | var res = "" 52 | _ = datasource.crawlingWeb(for: "https://en.wikipedia.org/wiki/Palworld") 53 | .sink(receiveCompletion: { 54 | switch $0 { 55 | case .finished: 56 | expectation.fulfill() 57 | print(res) 58 | XCTAssert(true, "success with \(res)") 59 | case let .failure(error): 60 | XCTAssert(false, "error with \(error.localizedDescription)") 61 | expectation.fulfill() 62 | } 63 | }, receiveValue: { value in 64 | res = value 65 | 66 | }) 67 | 68 | waitForExpectations(timeout: 20) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /OllamaGuiTest/Domain/Usecase/ChatUsecase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChatUsecase.swift 3 | // ollamaGuiTest 4 | // 5 | // Created by 배상휘 on 3/11/24. 6 | // 7 | 8 | import Foundation 9 | 10 | import Combine 11 | @testable import ollamaGUI 12 | import SwiftData 13 | import XCTest 14 | 15 | class ChatUsecaseTest: XCTestCase { 16 | var container: ModelContainer! 17 | var chatUsecase: ChatUsecase! 18 | var appSetting: AppSetting! 19 | 20 | override func setUp() { 21 | let config = ModelConfiguration(isStoredInMemoryOnly: true) 22 | container = try? ModelContainer(for: 23 | MessageEntity.self, ChatEntity.self, RoomEntity.self, 24 | AppSettingEntity.self, 25 | configurations: config) 26 | 27 | let sessionManager = SessionManager() 28 | let ollamaDataSource = OllamaDatasourceImpl( 29 | baseUrl: "", 30 | session: sessionManager.session 31 | ) 32 | appSetting = AppSetting(ollamaDs: ollamaDataSource) 33 | appSetting 34 | .updateSetting(.init(baseUrl: "http://localhost:11434", 35 | selectedModel: "mistral")) 36 | 37 | let ollamaRepository = 38 | OllamaRepositoryImpl(dataSource: ollamaDataSource) 39 | chatUsecase = ChatUsecase(ollamaRepository: ollamaRepository) 40 | } 41 | 42 | override func tearDown() { 43 | chatUsecase = nil 44 | container = nil 45 | } 46 | 47 | func test_채팅() { 48 | let ex = expectation(description: "wait for complete") 49 | let req = ChatRequestModel(of: .init(text: "hello")) 50 | var bag = Set() 51 | chatUsecase.chat(req: req, appSetting: appSetting, options: nil) 52 | .sink( 53 | receiveCompletion: { comp in 54 | switch comp { 55 | case .finished: 56 | ex.fulfill() 57 | } 58 | 59 | }, 60 | receiveValue: { val in 61 | switch val { 62 | case let .failed(err): 63 | print(err.localizedDescription) 64 | ex.fulfill() 65 | return 66 | default: 67 | print(val) 68 | } 69 | } 70 | ).store(in: &bag) 71 | 72 | waitForExpectations(timeout: 20) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /OllamaGuiTest/Domain/Usecase/Langchain+ChatUsecase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Langchain+ChatUsecase.swift 3 | // ollamaGuiTest 4 | // 5 | // Created by 배상휘 on 3/12/24. 6 | // 7 | 8 | import Combine 9 | import Foundation 10 | @testable import ollamaGUI 11 | import SwiftData 12 | import XCTest 13 | 14 | class LangChainChatUsecaseTest: XCTestCase { 15 | var container: ModelContainer! 16 | var chatUsecase: ChatUsecase! 17 | var appSetting: AppSetting! 18 | var langchainUsecase: LangchainUsecase! 19 | 20 | override func setUp() { 21 | let config = ModelConfiguration(isStoredInMemoryOnly: true) 22 | container = try? ModelContainer(for: 23 | MessageEntity.self, ChatEntity.self, RoomEntity.self, 24 | AppSettingEntity.self, 25 | configurations: config) 26 | 27 | let sessionManager = SessionManager() 28 | 29 | let ollamaDataSource = OllamaDatasourceImpl( 30 | baseUrl: appSetting.baseUrl, 31 | session: sessionManager.session 32 | ) 33 | appSetting = AppSetting(ollamaDs: ollamaDataSource) 34 | appSetting 35 | .updateSetting(.init(baseUrl: "http://localhost:11434", 36 | selectedModel: "mistral")) 37 | let webDataSource = WebDatasourceImpl(session: sessionManager.session) 38 | let ollamaRepository = 39 | OllamaRepositoryImpl(dataSource: ollamaDataSource) 40 | let crawlingRepository = 41 | CrawlingRepositoryImpl(datasource: webDataSource) 42 | chatUsecase = ChatUsecase(ollamaRepository: ollamaRepository) 43 | langchainUsecase = LangchainUsecase( 44 | embedingRepository: ollamaRepository, 45 | crawlingRepository: crawlingRepository 46 | ) 47 | } 48 | 49 | override func tearDown() { 50 | chatUsecase = nil 51 | langchainUsecase = nil 52 | appSetting = nil 53 | container = nil 54 | } 55 | 56 | func test_EmbeddingChat() { 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /OllamaGuiTest/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSAppTransportSecurity 6 | 7 | NSAllowsArbitraryLoads 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /OllamaGuiTest/ollamaGuiTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ollamaGuiTest.swift 3 | // ollamaGuiTest 4 | // 5 | // Created by 배상휘 on 3/8/24. 6 | // 7 | 8 | import XCTest 9 | @testable import ollamaGUI 10 | 11 | final class ollamaGuiTest: XCTestCase { 12 | 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDownWithError() throws { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testExample() throws { 22 | // This is an example of a functional test case. 23 | // Use XCTAssert and related functions to verify your tests produce the correct results. 24 | // Any test you write for XCTest can be annotated as throws and async. 25 | // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. 26 | // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. 27 | } 28 | 29 | func testPerformanceExample() throws { 30 | // This is an example of a performance test case. 31 | measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'ollamaGUI' do 5 | # Comment the next line if you don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for ollamaGUI 9 | pod 'SwiftUIIntrospect', '~> 1.0' 10 | pod 'Alamofire' 11 | pod 'SwiftSoup' 12 | 13 | end 14 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (5.8.1) 3 | - SwiftSoup (2.7.0) 4 | - SwiftUIIntrospect (1.1.1) 5 | 6 | DEPENDENCIES: 7 | - Alamofire 8 | - SwiftSoup 9 | - SwiftUIIntrospect (~> 1.0) 10 | 11 | SPEC REPOS: 12 | trunk: 13 | - Alamofire 14 | - SwiftSoup 15 | - SwiftUIIntrospect 16 | 17 | SPEC CHECKSUMS: 18 | Alamofire: 3ca42e259043ee0dc5c0cdd76c4bc568b8e42af7 19 | SwiftSoup: 06cbbf665257adfbfe0c927c63de246e5e844b9d 20 | SwiftUIIntrospect: db5290a3492424eb4afcab77cebcd4595de3bd71 21 | 22 | PODFILE CHECKSUM: fd8561c90a9346cbc2ae69807a3d7301b3553a18 23 | 24 | COCOAPODS: 1.14.3 25 | -------------------------------------------------------------------------------- /Pods/Alamofire/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2022 Alamofire Software Foundation (http://alamofire.org/) 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 11 | all 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 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2021 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Dispatch 26 | import Foundation 27 | #if canImport(FoundationNetworking) 28 | @_exported import FoundationNetworking 29 | #endif 30 | 31 | // Enforce minimum Swift version for all platforms and build systems. 32 | #if swift(<5.5) 33 | #error("Alamofire doesn't support Swift versions below 5.5.") 34 | #endif 35 | 36 | /// Reference to `Session.default` for quick bootstrapping and examples. 37 | public let AF = Session.default 38 | 39 | /// Current Alamofire version. Necessary since SPM doesn't use dynamic libraries. Plus this will be more accurate. 40 | let version = "5.8.0" 41 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/AlamofireExtended.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlamofireExtended.swift 3 | // 4 | // Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | /// Type that acts as a generic extension point for all `AlamofireExtended` types. 26 | public struct AlamofireExtension { 27 | /// Stores the type or meta-type of any extended type. 28 | public private(set) var type: ExtendedType 29 | 30 | /// Create an instance from the provided value. 31 | /// 32 | /// - Parameter type: Instance being extended. 33 | public init(_ type: ExtendedType) { 34 | self.type = type 35 | } 36 | } 37 | 38 | /// Protocol describing the `af` extension points for Alamofire extended types. 39 | public protocol AlamofireExtended { 40 | /// Type being extended. 41 | associatedtype ExtendedType 42 | 43 | /// Static Alamofire extension point. 44 | static var af: AlamofireExtension.Type { get set } 45 | /// Instance Alamofire extension point. 46 | var af: AlamofireExtension { get set } 47 | } 48 | 49 | extension AlamofireExtended { 50 | /// Static Alamofire extension point. 51 | public static var af: AlamofireExtension.Type { 52 | get { AlamofireExtension.self } 53 | set {} 54 | } 55 | 56 | /// Instance Alamofire extension point. 57 | public var af: AlamofireExtension { 58 | get { AlamofireExtension(self) } 59 | set {} 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/DispatchQueue+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DispatchQueue+Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Dispatch 26 | import Foundation 27 | 28 | extension DispatchQueue { 29 | /// Execute the provided closure after a `TimeInterval`. 30 | /// 31 | /// - Parameters: 32 | /// - delay: `TimeInterval` to delay execution. 33 | /// - closure: Closure to execute. 34 | func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) { 35 | asyncAfter(deadline: .now() + delay, execute: closure) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/HTTPMethod.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HTTPMethod.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | /// Type representing HTTP methods. Raw `String` value is stored and compared case-sensitively, so 26 | /// `HTTPMethod.get != HTTPMethod(rawValue: "get")`. 27 | /// 28 | /// See https://tools.ietf.org/html/rfc7231#section-4.3 29 | public struct HTTPMethod: RawRepresentable, Equatable, Hashable { 30 | /// `CONNECT` method. 31 | public static let connect = HTTPMethod(rawValue: "CONNECT") 32 | /// `DELETE` method. 33 | public static let delete = HTTPMethod(rawValue: "DELETE") 34 | /// `GET` method. 35 | public static let get = HTTPMethod(rawValue: "GET") 36 | /// `HEAD` method. 37 | public static let head = HTTPMethod(rawValue: "HEAD") 38 | /// `OPTIONS` method. 39 | public static let options = HTTPMethod(rawValue: "OPTIONS") 40 | /// `PATCH` method. 41 | public static let patch = HTTPMethod(rawValue: "PATCH") 42 | /// `POST` method. 43 | public static let post = HTTPMethod(rawValue: "POST") 44 | /// `PUT` method. 45 | public static let put = HTTPMethod(rawValue: "PUT") 46 | /// `QUERY` method. 47 | public static let query = HTTPMethod(rawValue: "QUERY") 48 | /// `TRACE` method. 49 | public static let trace = HTTPMethod(rawValue: "TRACE") 50 | 51 | public let rawValue: String 52 | 53 | public init(rawValue: String) { 54 | self.rawValue = rawValue 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/OperationQueue+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperationQueue+Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension OperationQueue { 28 | /// Creates an instance using the provided parameters. 29 | /// 30 | /// - Parameters: 31 | /// - qualityOfService: `QualityOfService` to be applied to the queue. `.default` by default. 32 | /// - maxConcurrentOperationCount: Maximum concurrent operations. 33 | /// `OperationQueue.defaultMaxConcurrentOperationCount` by default. 34 | /// - underlyingQueue: Underlying `DispatchQueue`. `nil` by default. 35 | /// - name: Name for the queue. `nil` by default. 36 | /// - startSuspended: Whether the queue starts suspended. `false` by default. 37 | convenience init(qualityOfService: QualityOfService = .default, 38 | maxConcurrentOperationCount: Int = OperationQueue.defaultMaxConcurrentOperationCount, 39 | underlyingQueue: DispatchQueue? = nil, 40 | name: String? = nil, 41 | startSuspended: Bool = false) { 42 | self.init() 43 | self.qualityOfService = qualityOfService 44 | self.maxConcurrentOperationCount = maxConcurrentOperationCount 45 | self.underlyingQueue = underlyingQueue 46 | self.name = name 47 | isSuspended = startSuspended 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/StringEncoding+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringEncoding+Alamofire.swift 3 | // 4 | // Copyright (c) 2020 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension String.Encoding { 28 | /// Creates an encoding from the IANA charset name. 29 | /// 30 | /// - Notes: These mappings match those [provided by CoreFoundation](https://opensource.apple.com/source/CF/CF-476.18/CFStringUtilities.c.auto.html) 31 | /// 32 | /// - Parameter name: IANA charset name. 33 | init?(ianaCharsetName name: String) { 34 | switch name.lowercased() { 35 | case "utf-8": 36 | self = .utf8 37 | case "iso-8859-1": 38 | self = .isoLatin1 39 | case "unicode-1-1", "iso-10646-ucs-2", "utf-16": 40 | self = .utf16 41 | case "utf-16be": 42 | self = .utf16BigEndian 43 | case "utf-16le": 44 | self = .utf16LittleEndian 45 | case "utf-32": 46 | self = .utf32 47 | case "utf-32be": 48 | self = .utf32BigEndian 49 | case "utf-32le": 50 | self = .utf32LittleEndian 51 | default: 52 | return nil 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/URLRequest+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLRequest+Alamofire.swift 3 | // 4 | // Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension URLRequest { 28 | /// Returns the `httpMethod` as Alamofire's `HTTPMethod` type. 29 | public var method: HTTPMethod? { 30 | get { httpMethod.map(HTTPMethod.init) } 31 | set { httpMethod = newValue?.rawValue } 32 | } 33 | 34 | public func validate() throws { 35 | if method == .get, let bodyData = httpBody { 36 | throw AFError.urlRequestValidationFailed(reason: .bodyDataInGETRequest(bodyData)) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/URLSessionConfiguration+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLSessionConfiguration+Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension URLSessionConfiguration: AlamofireExtended {} 28 | extension AlamofireExtension where ExtendedType: URLSessionConfiguration { 29 | /// Alamofire's default configuration. Same as `URLSessionConfiguration.default` but adds Alamofire default 30 | /// `Accept-Language`, `Accept-Encoding`, and `User-Agent` headers. 31 | public static var `default`: URLSessionConfiguration { 32 | let configuration = URLSessionConfiguration.default 33 | configuration.headers = .default 34 | 35 | return configuration 36 | } 37 | 38 | /// `.ephemeral` configuration with Alamofire's default `Accept-Language`, `Accept-Encoding`, and `User-Agent` 39 | /// headers. 40 | public static var ephemeral: URLSessionConfiguration { 41 | let configuration = URLSessionConfiguration.ephemeral 42 | configuration.headers = .default 43 | 44 | return configuration 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (5.8.1) 3 | - SwiftSoup (2.7.0) 4 | - SwiftUIIntrospect (1.1.1) 5 | 6 | DEPENDENCIES: 7 | - Alamofire 8 | - SwiftSoup 9 | - SwiftUIIntrospect (~> 1.0) 10 | 11 | SPEC REPOS: 12 | trunk: 13 | - Alamofire 14 | - SwiftSoup 15 | - SwiftUIIntrospect 16 | 17 | SPEC CHECKSUMS: 18 | Alamofire: 3ca42e259043ee0dc5c0cdd76c4bc568b8e42af7 19 | SwiftSoup: 06cbbf665257adfbfe0c927c63de246e5e844b9d 20 | SwiftUIIntrospect: db5290a3492424eb4afcab77cebcd4595de3bd71 21 | 22 | PODFILE CHECKSUM: fd8561c90a9346cbc2ae69807a3d7301b3553a18 23 | 24 | COCOAPODS: 1.14.3 25 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/baesanghwi.xcuserdatad/xcschemes/Alamofire.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 53 | 54 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/baesanghwi.xcuserdatad/xcschemes/Pods-ollamaGUI.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 53 | 54 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/baesanghwi.xcuserdatad/xcschemes/SwiftUIIntrospect.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 53 | 54 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/baesanghwi.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Alamofire.xcscheme 8 | 9 | isShown 10 | 11 | 12 | Pods-ollamaGUI.xcscheme 13 | 14 | isShown 15 | 16 | 17 | SwiftSoup.xcscheme 18 | 19 | isShown 20 | 21 | 22 | SwiftUIIntrospect.xcscheme 23 | 24 | isShown 25 | 26 | 27 | 28 | SuppressBuildableAutocreation 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Timber Software 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/IntrospectableViewType.swift: -------------------------------------------------------------------------------- 1 | #if !os(watchOS) 2 | public protocol IntrospectableViewType { 3 | /// The scope of introspection for this particular view type, i.e. where introspect 4 | /// should look to find the desired target view relative to the applied 5 | /// `.introspect(...)` modifier. 6 | /// 7 | /// While the scope can be overridden by the user in their `.introspect(...)` call, 8 | /// most of the time it's preferable to defer to the view type's own scope, 9 | /// as it guarantees introspection is working as intended by the vendor. 10 | /// 11 | /// Defaults to `.receiver` if left unimplemented, which is a sensible one in 12 | /// most cases if you're looking to implement your own view type. 13 | var scope: IntrospectionScope { get } 14 | } 15 | 16 | extension IntrospectableViewType { 17 | public var scope: IntrospectionScope { .receiver } 18 | } 19 | #endif 20 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/PlatformView.swift: -------------------------------------------------------------------------------- 1 | #if !os(watchOS) 2 | import SwiftUI 3 | 4 | #if canImport(UIKit) 5 | public typealias PlatformView = UIView 6 | #elseif canImport(AppKit) 7 | public typealias PlatformView = NSView 8 | #endif 9 | 10 | #if canImport(UIKit) 11 | public typealias PlatformViewController = UIViewController 12 | #elseif canImport(AppKit) 13 | public typealias PlatformViewController = NSViewController 14 | #endif 15 | 16 | #if canImport(UIKit) 17 | typealias _PlatformViewControllerRepresentable = UIViewControllerRepresentable 18 | #elseif canImport(AppKit) 19 | typealias _PlatformViewControllerRepresentable = NSViewControllerRepresentable 20 | #endif 21 | 22 | protocol PlatformViewControllerRepresentable: _PlatformViewControllerRepresentable { 23 | #if canImport(UIKit) 24 | typealias ViewController = UIViewControllerType 25 | #elseif canImport(AppKit) 26 | typealias ViewController = NSViewControllerType 27 | #endif 28 | 29 | func makePlatformViewController(context: Context) -> ViewController 30 | func updatePlatformViewController(_ controller: ViewController, context: Context) 31 | static func dismantlePlatformViewController(_ controller: ViewController, coordinator: Coordinator) 32 | } 33 | 34 | extension PlatformViewControllerRepresentable { 35 | #if canImport(UIKit) 36 | func makeUIViewController(context: Context) -> ViewController { 37 | makePlatformViewController(context: context) 38 | } 39 | func updateUIViewController(_ controller: ViewController, context: Context) { 40 | updatePlatformViewController(controller, context: context) 41 | } 42 | static func dismantleUIViewController(_ controller: ViewController, coordinator: Coordinator) { 43 | dismantlePlatformViewController(controller, coordinator: coordinator) 44 | } 45 | #elseif canImport(AppKit) 46 | func makeNSViewController(context: Context) -> ViewController { 47 | makePlatformViewController(context: context) 48 | } 49 | func updateNSViewController(_ controller: ViewController, context: Context) { 50 | updatePlatformViewController(controller, context: context) 51 | } 52 | static func dismantleNSViewController(_ controller: ViewController, coordinator: Coordinator) { 53 | dismantlePlatformViewController(controller, coordinator: coordinator) 54 | } 55 | #endif 56 | } 57 | #endif 58 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/Utils.swift: -------------------------------------------------------------------------------- 1 | postfix operator ~ 2 | 3 | postfix func ~ (lhs: LHS) -> T { 4 | lhs as! T 5 | } 6 | 7 | postfix func ~ (lhs: LHS?) -> T? { 8 | lhs as? T 9 | } 10 | 11 | func recursiveSequence(_ sequence: S, children: @escaping (S.Element) -> S) -> AnySequence { 12 | AnySequence { 13 | var mainIterator = sequence.makeIterator() 14 | // Current iterator, or `nil` if all sequences are exhausted: 15 | var iterator: AnyIterator? 16 | 17 | return AnyIterator { 18 | guard let iterator, let element = iterator.next() else { 19 | if let element = mainIterator.next() { 20 | iterator = recursiveSequence(children(element), children: children).makeIterator() 21 | return element 22 | } 23 | return nil 24 | } 25 | return element 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/ViewTypes/Button.swift: -------------------------------------------------------------------------------- 1 | #if !os(watchOS) 2 | import SwiftUI 3 | 4 | /// An abstract representation of the `Button` type in SwiftUI. 5 | /// 6 | /// ### iOS 7 | /// 8 | /// Not available. 9 | /// 10 | /// ### tvOS 11 | /// 12 | /// Not available. 13 | /// 14 | /// ### macOS 15 | /// 16 | /// ```swift 17 | /// struct ContentView: View { 18 | /// var body: some View { 19 | /// Button("Action", action: {}) 20 | /// .introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { 21 | /// print(type(of: $0)) // NSButton 22 | /// } 23 | /// } 24 | /// } 25 | /// ``` 26 | /// 27 | /// ### visionOS 28 | /// 29 | /// Not available. 30 | public struct ButtonType: IntrospectableViewType {} 31 | 32 | #if !os(iOS) && !os(tvOS) && !os(visionOS) 33 | extension IntrospectableViewType where Self == ButtonType { 34 | public static var button: Self { .init() } 35 | } 36 | 37 | #if canImport(AppKit) && !targetEnvironment(macCatalyst) 38 | extension macOSViewVersion { 39 | public static let v10_15 = Self(for: .v10_15) 40 | public static let v11 = Self(for: .v11) 41 | public static let v12 = Self(for: .v12) 42 | public static let v13 = Self(for: .v13) 43 | public static let v14 = Self(for: .v14) 44 | } 45 | #endif 46 | #endif 47 | #endif 48 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/ViewTypes/ColorPicker.swift: -------------------------------------------------------------------------------- 1 | #if !os(watchOS) 2 | import SwiftUI 3 | 4 | /// An abstract representation of the `ColorPicker` type in SwiftUI. 5 | /// 6 | /// ### iOS 7 | /// 8 | /// ```swift 9 | /// struct ContentView: View { 10 | /// @State var color = Color.red 11 | /// 12 | /// var body: some View { 13 | /// ColorPicker("Pick a color", selection: $color) 14 | /// .introspect(.colorPicker, on: .iOS(.v14, .v15, .v16, .v17)) { 15 | /// print(type(of: $0)) // UIColorPicker 16 | /// } 17 | /// } 18 | /// } 19 | /// ``` 20 | /// 21 | /// ### tvOS 22 | /// 23 | /// Not available. 24 | /// 25 | /// ### macOS 26 | /// 27 | /// ```swift 28 | /// struct ContentView: View { 29 | /// @State var color = Color.red 30 | /// 31 | /// var body: some View { 32 | /// ColorPicker("Pick a color", selection: $color) 33 | /// .introspect(.colorPicker, on: .macOS(.v11, .v12, .v13, .v14)) { 34 | /// print(type(of: $0)) // NSColorPicker 35 | /// } 36 | /// } 37 | /// } 38 | /// ``` 39 | /// 40 | /// ### visionOS 41 | /// 42 | /// ```swift 43 | /// struct ContentView: View { 44 | /// @State var color = Color.red 45 | /// 46 | /// var body: some View { 47 | /// ColorPicker("Pick a color", selection: $color) 48 | /// .introspect(.colorPicker, on: .visionOS(.v1)) { 49 | /// print(type(of: $0)) // UIColorPicker 50 | /// } 51 | /// } 52 | /// } 53 | /// ``` 54 | public struct ColorPickerType: IntrospectableViewType {} 55 | 56 | #if !os(tvOS) 57 | extension IntrospectableViewType where Self == ColorPickerType { 58 | public static var colorPicker: Self { .init() } 59 | } 60 | 61 | #if canImport(UIKit) 62 | @available(iOS 14, *) 63 | extension iOSViewVersion { 64 | @available(*, unavailable, message: "ColorPicker isn't available on iOS 13") 65 | public static let v13 = Self.unavailable() 66 | public static let v14 = Self(for: .v14) 67 | public static let v15 = Self(for: .v15) 68 | public static let v16 = Self(for: .v16) 69 | public static let v17 = Self(for: .v17) 70 | } 71 | 72 | @available(iOS 14, *) 73 | extension visionOSViewVersion { 74 | public static let v1 = Self(for: .v1) 75 | } 76 | #elseif canImport(AppKit) 77 | @available(macOS 11, *) 78 | extension macOSViewVersion { 79 | @available(*, unavailable, message: "ColorPicker isn't available on macOS 10.15") 80 | public static let v10_15 = Self.unavailable() 81 | public static let v11 = Self(for: .v11) 82 | public static let v12 = Self(for: .v12) 83 | public static let v13 = Self(for: .v13) 84 | public static let v14 = Self(for: .v14) 85 | } 86 | #endif 87 | #endif 88 | #endif 89 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/ViewTypes/DatePicker.swift: -------------------------------------------------------------------------------- 1 | #if !os(watchOS) 2 | import SwiftUI 3 | 4 | /// An abstract representation of the `DatePicker` type in SwiftUI. 5 | /// 6 | /// ### iOS 7 | /// 8 | /// ```swift 9 | /// struct ContentView: View { 10 | /// @State var date = Date() 11 | /// 12 | /// var body: some View { 13 | /// DatePicker("Pick a date", selection: $date) 14 | /// .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { 15 | /// print(type(of: $0)) // UIDatePicker 16 | /// } 17 | /// } 18 | /// } 19 | /// ``` 20 | /// 21 | /// ### tvOS 22 | /// 23 | /// Not available. 24 | /// 25 | /// ```swift 26 | /// struct ContentView: View { 27 | /// @State var date = Date() 28 | /// 29 | /// var body: some View { 30 | /// DatePicker("Pick a date", selection: $date) 31 | /// .introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { 32 | /// print(type(of: $0)) // NSDatePicker 33 | /// } 34 | /// } 35 | /// } 36 | /// ``` 37 | /// 38 | /// ### visionOS 39 | /// 40 | /// ```swift 41 | /// struct ContentView: View { 42 | /// @State var date = Date() 43 | /// 44 | /// var body: some View { 45 | /// DatePicker("Pick a date", selection: $date) 46 | /// .introspect(.datePicker, on: .visionOS(.v1)) { 47 | /// print(type(of: $0)) // UIDatePicker 48 | /// } 49 | /// } 50 | /// } 51 | /// ``` 52 | public struct DatePickerType: IntrospectableViewType {} 53 | 54 | #if !os(tvOS) 55 | extension IntrospectableViewType where Self == DatePickerType { 56 | public static var datePicker: Self { .init() } 57 | } 58 | 59 | #if canImport(UIKit) 60 | extension iOSViewVersion { 61 | public static let v13 = Self(for: .v13) 62 | public static let v14 = Self(for: .v14) 63 | public static let v15 = Self(for: .v15) 64 | public static let v16 = Self(for: .v16) 65 | public static let v17 = Self(for: .v17) 66 | } 67 | 68 | extension visionOSViewVersion { 69 | public static let v1 = Self(for: .v1) 70 | } 71 | #elseif canImport(AppKit) 72 | extension macOSViewVersion { 73 | public static let v10_15 = Self(for: .v10_15) 74 | public static let v11 = Self(for: .v11) 75 | public static let v12 = Self(for: .v12) 76 | public static let v13 = Self(for: .v13) 77 | public static let v14 = Self(for: .v14) 78 | } 79 | #endif 80 | #endif 81 | #endif 82 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/ViewTypes/DatePickerWithFieldStyle.swift: -------------------------------------------------------------------------------- 1 | #if !os(watchOS) 2 | import SwiftUI 3 | 4 | /// An abstract representation of the `DatePicker` type in SwiftUI, with `.field` style. 5 | /// 6 | /// ### iOS 7 | /// 8 | /// Not available. 9 | /// 10 | /// ### tvOS 11 | /// 12 | /// Not available. 13 | /// 14 | /// ### macOS 15 | /// 16 | /// ```swift 17 | /// struct ContentView: View { 18 | /// @State var date = Date() 19 | /// 20 | /// var body: some View { 21 | /// DatePicker("Pick a date", selection: $date) 22 | /// .datePickerStyle(.field) 23 | /// .introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { 24 | /// print(type(of: $0)) // NSDatePicker 25 | /// } 26 | /// } 27 | /// } 28 | /// ``` 29 | /// 30 | /// ### visionOS 31 | /// 32 | /// Not available. 33 | public struct DatePickerWithFieldStyleType: IntrospectableViewType { 34 | public enum Style { 35 | case field 36 | } 37 | } 38 | 39 | #if !os(iOS) && !os(tvOS) && !os(visionOS) 40 | extension IntrospectableViewType where Self == DatePickerWithFieldStyleType { 41 | public static func datePicker(style: Self.Style) -> Self { .init() } 42 | } 43 | 44 | #if canImport(AppKit) && !targetEnvironment(macCatalyst) 45 | extension macOSViewVersion { 46 | public static let v10_15 = Self(for: .v10_15) 47 | public static let v11 = Self(for: .v11) 48 | public static let v12 = Self(for: .v12) 49 | public static let v13 = Self(for: .v13) 50 | public static let v14 = Self(for: .v14) 51 | } 52 | #endif 53 | #endif 54 | #endif 55 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift: -------------------------------------------------------------------------------- 1 | #if !os(watchOS) 2 | import SwiftUI 3 | 4 | /// An abstract representation of the `DatePicker` type in SwiftUI, with `.stepperField` style. 5 | /// 6 | /// ### iOS 7 | /// 8 | /// Not available. 9 | /// 10 | /// ### tvOS 11 | /// 12 | /// Not available. 13 | /// 14 | /// ### macOS 15 | /// 16 | /// ```swift 17 | /// struct ContentView: View { 18 | /// @State var date = Date() 19 | /// 20 | /// var body: some View { 21 | /// DatePicker("Pick a date", selection: $date) 22 | /// .datePickerStyle(.stepperField) 23 | /// .introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { 24 | /// print(type(of: $0)) // NSDatePicker 25 | /// } 26 | /// } 27 | /// } 28 | /// ``` 29 | /// 30 | /// ### visionOS 31 | /// 32 | /// Not available. 33 | public struct DatePickerWithStepperFieldStyleType: IntrospectableViewType { 34 | public enum Style { 35 | case stepperField 36 | } 37 | } 38 | 39 | #if !os(iOS) && !os(tvOS) && !os(visionOS) 40 | extension IntrospectableViewType where Self == DatePickerWithStepperFieldStyleType { 41 | public static func datePicker(style: Self.Style) -> Self { .init() } 42 | } 43 | 44 | #if canImport(AppKit) && !targetEnvironment(macCatalyst) 45 | extension macOSViewVersion { 46 | public static let v10_15 = Self(for: .v10_15) 47 | public static let v11 = Self(for: .v11) 48 | public static let v12 = Self(for: .v12) 49 | public static let v13 = Self(for: .v13) 50 | public static let v14 = Self(for: .v14) 51 | } 52 | #endif 53 | #endif 54 | #endif 55 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/ViewTypes/DatePickerWithWheelStyle.swift: -------------------------------------------------------------------------------- 1 | #if !os(watchOS) 2 | import SwiftUI 3 | 4 | /// An abstract representation of the `DatePicker` type in SwiftUI, with `.wheel` style. 5 | /// 6 | /// ### iOS 7 | /// 8 | /// ```swift 9 | /// struct ContentView: View { 10 | /// @State var date = Date() 11 | /// 12 | /// var body: some View { 13 | /// DatePicker("Pick a date", selection: $date) 14 | /// .datePickerStyle(.wheel) 15 | /// .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { 16 | /// print(type(of: $0)) // UIDatePicker 17 | /// } 18 | /// } 19 | /// } 20 | /// ``` 21 | /// 22 | /// ### tvOS 23 | /// 24 | /// Not available. 25 | /// 26 | /// ### macOS 27 | /// 28 | /// Not available. 29 | /// 30 | /// ### visionOS 31 | /// 32 | /// ```swift 33 | /// struct ContentView: View { 34 | /// @State var date = Date() 35 | /// 36 | /// var body: some View { 37 | /// DatePicker("Pick a date", selection: $date) 38 | /// .datePickerStyle(.wheel) 39 | /// .introspect(.datePicker(style: .wheel), on: .visionOS(.v1)) { 40 | /// print(type(of: $0)) // UIDatePicker 41 | /// } 42 | /// } 43 | /// } 44 | /// ``` 45 | public struct DatePickerWithWheelStyleType: IntrospectableViewType { 46 | public enum Style { 47 | case wheel 48 | } 49 | } 50 | 51 | #if !os(tvOS) && !os(macOS) 52 | extension IntrospectableViewType where Self == DatePickerWithWheelStyleType { 53 | public static func datePicker(style: Self.Style) -> Self { .init() } 54 | } 55 | 56 | #if canImport(UIKit) 57 | extension iOSViewVersion { 58 | public static let v13 = Self(for: .v13) 59 | public static let v14 = Self(for: .v14) 60 | public static let v15 = Self(for: .v15) 61 | public static let v16 = Self(for: .v16) 62 | public static let v17 = Self(for: .v17) 63 | } 64 | 65 | extension visionOSViewVersion { 66 | public static let v1 = Self(for: .v1) 67 | } 68 | #endif 69 | #endif 70 | #endif 71 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/ViewTypes/Form.swift: -------------------------------------------------------------------------------- 1 | #if !os(watchOS) 2 | import SwiftUI 3 | 4 | /// An abstract representation of the `Form` type in SwiftUI. 5 | /// 6 | /// ### iOS 7 | /// 8 | /// ```swift 9 | /// struct ContentView: View { 10 | /// var body: some View { 11 | /// Form { 12 | /// Text("Item 1") 13 | /// Text("Item 2") 14 | /// Text("Item 3") 15 | /// } 16 | /// .introspect(.form, on: .iOS(.v13, .v14, .v15)) { 17 | /// print(type(of: $0)) // UITableView 18 | /// } 19 | /// .introspect(.form, on: .iOS(.v16, .v17)) { 20 | /// print(type(of: $0)) // UICollectionView 21 | /// } 22 | /// } 23 | /// } 24 | /// ``` 25 | /// 26 | /// ### tvOS 27 | /// 28 | /// ```swift 29 | /// struct ContentView: View { 30 | /// var body: some View { 31 | /// Form { 32 | /// Text("Item 1") 33 | /// Text("Item 2") 34 | /// Text("Item 3") 35 | /// } 36 | /// .introspect(.form, on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { 37 | /// print(type(of: $0)) // UITableView 38 | /// } 39 | /// } 40 | /// } 41 | /// ``` 42 | /// 43 | /// ### macOS 44 | /// 45 | /// Not available. 46 | /// 47 | /// ### visionOS 48 | /// 49 | /// ```swift 50 | /// struct ContentView: View { 51 | /// var body: some View { 52 | /// Form { 53 | /// Text("Item 1") 54 | /// Text("Item 2") 55 | /// Text("Item 3") 56 | /// } 57 | /// .introspect(.form, on: .visionOS(.v1)) { 58 | /// print(type(of: $0)) // UICollectionView 59 | /// } 60 | /// } 61 | /// } 62 | /// ``` 63 | public struct FormType: IntrospectableViewType {} 64 | 65 | #if !os(macOS) 66 | extension IntrospectableViewType where Self == FormType { 67 | public static var form: Self { .init() } 68 | } 69 | 70 | #if canImport(UIKit) 71 | extension iOSViewVersion { 72 | public static let v13 = Self(for: .v13) 73 | public static let v14 = Self(for: .v14) 74 | public static let v15 = Self(for: .v15) 75 | } 76 | 77 | extension iOSViewVersion { 78 | public static let v16 = Self(for: .v16) 79 | public static let v17 = Self(for: .v17) 80 | } 81 | 82 | extension tvOSViewVersion { 83 | public static let v13 = Self(for: .v13) 84 | public static let v14 = Self(for: .v14) 85 | public static let v15 = Self(for: .v15) 86 | public static let v16 = Self(for: .v16) 87 | public static let v17 = Self(for: .v17) 88 | } 89 | 90 | extension visionOSViewVersion { 91 | public static let v1 = Self(for: .v1) 92 | } 93 | #endif 94 | #endif 95 | #endif 96 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/ViewTypes/ListWithBorderedStyle.swift: -------------------------------------------------------------------------------- 1 | #if !os(watchOS) 2 | import SwiftUI 3 | 4 | /// An abstract representation of the `List` type in SwiftUI, with `.bordered` style. 5 | /// 6 | /// ### iOS 7 | /// 8 | /// Not available. 9 | /// 10 | /// ### tvOS 11 | /// 12 | /// Not available. 13 | /// 14 | /// ### macOS 15 | /// 16 | /// ```swift 17 | /// struct ContentView: View { 18 | /// var body: some View { 19 | /// List { 20 | /// Text("Item 1") 21 | /// Text("Item 2") 22 | /// Text("Item 3") 23 | /// } 24 | /// .listStyle(.bordered) 25 | /// .introspect(.list(style: .bordered), on: .macOS(.v12, .v13, .v14)) { 26 | /// print(type(of: $0)) // NSTableView 27 | /// } 28 | /// } 29 | /// } 30 | /// ``` 31 | /// 32 | /// ### visionOS 33 | /// 34 | /// Not available. 35 | public struct ListWithBorderedStyleType: IntrospectableViewType { 36 | public enum Style { 37 | case bordered 38 | } 39 | } 40 | 41 | #if !os(iOS) && !os(tvOS) && !os(visionOS) 42 | extension IntrospectableViewType where Self == ListWithBorderedStyleType { 43 | public static func list(style: Self.Style) -> Self { .init() } 44 | } 45 | 46 | #if canImport(AppKit) && !targetEnvironment(macCatalyst) 47 | extension macOSViewVersion { 48 | @available(*, unavailable, message: ".listStyle(.insetGrouped) isn't available on macOS 10.15") 49 | public static let v10_15 = Self.unavailable() 50 | @available(*, unavailable, message: ".listStyle(.insetGrouped) isn't available on macOS 11") 51 | public static let v11 = Self.unavailable() 52 | public static let v12 = Self(for: .v12) 53 | public static let v13 = Self(for: .v13) 54 | public static let v14 = Self(for: .v14) 55 | } 56 | #endif 57 | #endif 58 | #endif 59 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/ViewTypes/ListWithInsetGroupedStyle.swift: -------------------------------------------------------------------------------- 1 | #if !os(watchOS) 2 | import SwiftUI 3 | 4 | /// An abstract representation of the `List` type in SwiftUI, with `.insetGrouped` style. 5 | /// 6 | /// ### iOS 7 | /// 8 | /// ```swift 9 | /// struct ContentView: View { 10 | /// var body: some View { 11 | /// List { 12 | /// Text("Item 1") 13 | /// Text("Item 2") 14 | /// Text("Item 3") 15 | /// } 16 | /// .listStyle(.insetGrouped) 17 | /// .introspect(.list(style: .insetGrouped), on: .iOS(.v14, .v15)) { 18 | /// print(type(of: $0)) // UITableView 19 | /// } 20 | /// .introspect(.list(style: .insetGrouped), on: .iOS(.v16, .v17)) { 21 | /// print(type(of: $0)) // UICollectionView 22 | /// } 23 | /// } 24 | /// } 25 | /// ``` 26 | /// 27 | /// ### tvOS 28 | /// 29 | /// Not available. 30 | /// 31 | /// ### macOS 32 | /// 33 | /// Not available. 34 | /// 35 | /// ### visionOS 36 | /// 37 | /// ```swift 38 | /// struct ContentView: View { 39 | /// var body: some View { 40 | /// List { 41 | /// Text("Item 1") 42 | /// Text("Item 2") 43 | /// Text("Item 3") 44 | /// } 45 | /// .listStyle(.insetGrouped) 46 | /// .introspect(.list(style: .insetGrouped), on: .visionOS(.v1)) { 47 | /// print(type(of: $0)) // UICollectionView 48 | /// } 49 | /// } 50 | /// } 51 | /// ``` 52 | public struct ListWithInsetGroupedStyleType: IntrospectableViewType { 53 | public enum Style { 54 | case insetGrouped 55 | } 56 | } 57 | 58 | #if !os(tvOS) && !os(macOS) 59 | extension IntrospectableViewType where Self == ListWithInsetGroupedStyleType { 60 | public static func list(style: Self.Style) -> Self { .init() } 61 | } 62 | 63 | #if canImport(UIKit) 64 | extension iOSViewVersion { 65 | @available(*, unavailable, message: ".listStyle(.insetGrouped) isn't available on iOS 13") 66 | public static let v13 = Self(for: .v13) 67 | public static let v14 = Self(for: .v14) 68 | public static let v15 = Self(for: .v15) 69 | } 70 | 71 | extension iOSViewVersion { 72 | public static let v16 = Self(for: .v16) 73 | public static let v17 = Self(for: .v17) 74 | } 75 | 76 | extension visionOSViewVersion { 77 | public static let v1 = Self(for: .v1) 78 | } 79 | #endif 80 | #endif 81 | #endif 82 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/ViewTypes/PickerWithMenuStyle.swift: -------------------------------------------------------------------------------- 1 | #if !os(watchOS) 2 | import SwiftUI 3 | 4 | /// An abstract representation of the `Picker` type in SwiftUI, with `.menu` style. 5 | /// 6 | /// ### iOS 7 | /// 8 | /// Not available. 9 | /// 10 | /// ### tvOS 11 | /// 12 | /// Not available. 13 | /// 14 | /// ### macOS 15 | /// 16 | /// ```swift 17 | /// struct ContentView: View { 18 | /// @State var selection = "1" 19 | /// 20 | /// var body: some View { 21 | /// Picker("Pick a number", selection: $selection) { 22 | /// Text("1").tag("1") 23 | /// Text("2").tag("2") 24 | /// Text("3").tag("3") 25 | /// } 26 | /// .pickerStyle(.menu) 27 | /// .introspect(.picker(style: .menu), on: .macOS(.v11, .v12, .v13, .v14)) { 28 | /// print(type(of: $0)) // NSPopUpButton 29 | /// } 30 | /// } 31 | /// } 32 | /// ``` 33 | /// 34 | /// ### visionOS 35 | /// 36 | /// Not available. 37 | public struct PickerWithMenuStyleType: IntrospectableViewType { 38 | public enum Style { 39 | case menu 40 | } 41 | } 42 | 43 | #if !os(iOS) && !os(tvOS) && !os(visionOS) 44 | extension IntrospectableViewType where Self == PickerWithMenuStyleType { 45 | public static func picker(style: Self.Style) -> Self { .init() } 46 | } 47 | 48 | #if canImport(AppKit) && !targetEnvironment(macCatalyst) 49 | extension macOSViewVersion { 50 | @available(*, unavailable, message: ".pickerStyle(.menu) isn't available on macOS 10.15") 51 | public static let v10_15 = Self.unavailable() 52 | public static let v11 = Self(for: .v11) 53 | public static let v12 = Self(for: .v12) 54 | public static let v13 = Self(for: .v13) 55 | public static let v14 = Self(for: .v14) 56 | } 57 | #endif 58 | #endif 59 | #endif 60 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/ViewTypes/PickerWithWheelStyle.swift: -------------------------------------------------------------------------------- 1 | #if !os(watchOS) 2 | import SwiftUI 3 | 4 | /// An abstract representation of the `Picker` type in SwiftUI, with `.wheel` style. 5 | /// 6 | /// ### iOS 7 | /// 8 | /// ```swift 9 | /// struct ContentView: View { 10 | /// @State var selection = "1" 11 | /// 12 | /// var body: some View { 13 | /// Picker("Pick a number", selection: $selection) { 14 | /// Text("1").tag("1") 15 | /// Text("2").tag("2") 16 | /// Text("3").tag("3") 17 | /// } 18 | /// .pickerStyle(.wheel) 19 | /// .introspect(.picker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { 20 | /// print(type(of: $0)) // UIPickerView 21 | /// } 22 | /// } 23 | /// } 24 | /// ``` 25 | /// 26 | /// ### tvOS 27 | /// 28 | /// Not available. 29 | /// 30 | /// ### macOS 31 | /// 32 | /// Not available. 33 | /// 34 | /// ### visionOS 35 | /// 36 | /// ```swift 37 | /// struct ContentView: View { 38 | /// @State var selection = "1" 39 | /// 40 | /// var body: some View { 41 | /// Picker("Pick a number", selection: $selection) { 42 | /// Text("1").tag("1") 43 | /// Text("2").tag("2") 44 | /// Text("3").tag("3") 45 | /// } 46 | /// .pickerStyle(.wheel) 47 | /// .introspect(.picker(style: .wheel), on: .visionOS(.v1)) { 48 | /// print(type(of: $0)) // UIPickerView 49 | /// } 50 | /// } 51 | /// } 52 | /// ``` 53 | public struct PickerWithWheelStyleType: IntrospectableViewType { 54 | public enum Style { 55 | case wheel 56 | } 57 | } 58 | 59 | #if !os(tvOS) && !os(macOS) 60 | extension IntrospectableViewType where Self == PickerWithWheelStyleType { 61 | public static func picker(style: Self.Style) -> Self { .init() } 62 | } 63 | 64 | #if canImport(UIKit) 65 | extension iOSViewVersion { 66 | public static let v13 = Self(for: .v13) 67 | public static let v14 = Self(for: .v14) 68 | public static let v15 = Self(for: .v15) 69 | public static let v16 = Self(for: .v16) 70 | public static let v17 = Self(for: .v17) 71 | } 72 | 73 | extension visionOSViewVersion { 74 | public static let v1 = Self(for: .v1) 75 | } 76 | #endif 77 | #endif 78 | #endif 79 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/ViewTypes/Popover.swift: -------------------------------------------------------------------------------- 1 | #if !os(watchOS) 2 | import SwiftUI 3 | 4 | /// An abstract representation of `.popover` in SwiftUI. 5 | /// 6 | /// ### iOS 7 | /// 8 | /// ```swift 9 | /// struct ContentView: View { 10 | /// @State var isPresented = false 11 | /// 12 | /// var body: some View { 13 | /// Button("Present", action: { isPresented = true }) 14 | /// .popover(isPresented: $isPresented) { 15 | /// Button("Dismiss", action: { isPresented = false }) 16 | /// .introspect(.popover, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { 17 | /// print(type(of: $0)) // UIPopoverPresentationController 18 | /// } 19 | /// } 20 | /// } 21 | /// } 22 | /// ``` 23 | /// 24 | /// ### tvOS 25 | /// 26 | /// Not available. 27 | /// 28 | /// ### macOS 29 | /// 30 | /// Not available. 31 | /// 32 | /// ### visionOS 33 | /// 34 | /// ```swift 35 | /// struct ContentView: View { 36 | /// @State var isPresented = false 37 | /// 38 | /// var body: some View { 39 | /// Button("Present", action: { isPresented = true }) 40 | /// .popover(isPresented: $isPresented) { 41 | /// Button("Dismiss", action: { isPresented = false }) 42 | /// .introspect(.popover, on: .visionOS(.v1)) { 43 | /// print(type(of: $0)) // UIPopoverPresentationController 44 | /// } 45 | /// } 46 | /// } 47 | /// } 48 | /// ``` 49 | public struct PopoverType: IntrospectableViewType { 50 | public var scope: IntrospectionScope { .ancestor } 51 | } 52 | 53 | #if !os(tvOS) && !os(macOS) 54 | extension IntrospectableViewType where Self == PopoverType { 55 | public static var popover: Self { .init() } 56 | } 57 | 58 | #if canImport(UIKit) 59 | extension iOSViewVersion { 60 | public static let v13 = Self(for: .v13, selector: selector) 61 | public static let v14 = Self(for: .v14, selector: selector) 62 | public static let v15 = Self(for: .v15, selector: selector) 63 | public static let v16 = Self(for: .v16, selector: selector) 64 | public static let v17 = Self(for: .v17, selector: selector) 65 | 66 | private static var selector: IntrospectionSelector { 67 | .from(UIViewController.self, selector: \.popoverPresentationController) 68 | } 69 | } 70 | 71 | extension visionOSViewVersion { 72 | public static let v1 = Self(for: .v1, selector: selector) 73 | 74 | private static var selector: IntrospectionSelector { 75 | .from(UIViewController.self, selector: \.popoverPresentationController) 76 | } 77 | } 78 | #endif 79 | #endif 80 | #endif 81 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/ViewTypes/Slider.swift: -------------------------------------------------------------------------------- 1 | #if !os(watchOS) 2 | import SwiftUI 3 | 4 | /// An abstract representation of the `Slider` type in SwiftUI. 5 | /// 6 | /// ### iOS 7 | /// 8 | /// ```swift 9 | /// struct ContentView: View { 10 | /// @State var selection = 0.5 11 | /// 12 | /// var body: some View { 13 | /// Slider(value: $selection, in: 0...1) 14 | /// .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { 15 | /// print(type(of: $0)) // UISlider 16 | /// } 17 | /// } 18 | /// } 19 | /// ``` 20 | /// 21 | /// ### tvOS 22 | /// 23 | /// Not available. 24 | /// 25 | /// ### macOS 26 | /// 27 | /// ```swift 28 | /// struct ContentView: View { 29 | /// @State var selection = 0.5 30 | /// 31 | /// var body: some View { 32 | /// Slider(value: $selection, in: 0...1) 33 | /// .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { 34 | /// print(type(of: $0)) // NSSlider 35 | /// } 36 | /// } 37 | /// } 38 | /// ``` 39 | /// 40 | /// ### visionOS 41 | /// 42 | /// Not available. 43 | public struct SliderType: IntrospectableViewType {} 44 | 45 | #if !os(tvOS) && !os(visionOS) 46 | extension IntrospectableViewType where Self == SliderType { 47 | public static var slider: Self { .init() } 48 | } 49 | 50 | #if canImport(UIKit) 51 | extension iOSViewVersion { 52 | public static let v13 = Self(for: .v13) 53 | public static let v14 = Self(for: .v14) 54 | public static let v15 = Self(for: .v15) 55 | public static let v16 = Self(for: .v16) 56 | public static let v17 = Self(for: .v17) 57 | } 58 | #elseif canImport(AppKit) 59 | extension macOSViewVersion { 60 | public static let v10_15 = Self(for: .v10_15) 61 | public static let v11 = Self(for: .v11) 62 | public static let v12 = Self(for: .v12) 63 | public static let v13 = Self(for: .v13) 64 | public static let v14 = Self(for: .v14) 65 | } 66 | #endif 67 | #endif 68 | #endif 69 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/ViewTypes/Stepper.swift: -------------------------------------------------------------------------------- 1 | #if !os(watchOS) 2 | import SwiftUI 3 | 4 | /// An abstract representation of the `Stepper` type in SwiftUI. 5 | /// 6 | /// ### iOS 7 | /// 8 | /// ```swift 9 | /// struct ContentView: View { 10 | /// @State var selection = 5 11 | /// 12 | /// var body: some View { 13 | /// Stepper("Select a number", value: $selection, in: 0...10) 14 | /// .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { 15 | /// print(type(of: $0)) // UIStepper 16 | /// } 17 | /// } 18 | /// } 19 | /// ``` 20 | /// 21 | /// ### tvOS 22 | /// 23 | /// Not available. 24 | /// 25 | /// ### macOS 26 | /// 27 | /// ```swift 28 | /// struct ContentView: View { 29 | /// @State var selection = 5 30 | /// 31 | /// var body: some View { 32 | /// Stepper("Select a number", value: $selection, in: 0...10) 33 | /// .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { 34 | /// print(type(of: $0)) // NSStepper 35 | /// } 36 | /// } 37 | /// } 38 | /// ``` 39 | /// 40 | /// ### visionOS 41 | /// 42 | /// Not available. 43 | public struct StepperType: IntrospectableViewType {} 44 | 45 | #if !os(tvOS) && !os(visionOS) 46 | extension IntrospectableViewType where Self == StepperType { 47 | public static var stepper: Self { .init() } 48 | } 49 | 50 | #if canImport(UIKit) 51 | extension iOSViewVersion { 52 | public static let v13 = Self(for: .v13) 53 | public static let v14 = Self(for: .v14) 54 | public static let v15 = Self(for: .v15) 55 | public static let v16 = Self(for: .v16) 56 | public static let v17 = Self(for: .v17) 57 | } 58 | #elseif canImport(AppKit) 59 | extension macOSViewVersion { 60 | public static let v10_15 = Self(for: .v10_15) 61 | public static let v11 = Self(for: .v11) 62 | public static let v12 = Self(for: .v12) 63 | public static let v13 = Self(for: .v13) 64 | public static let v14 = Self(for: .v14) 65 | } 66 | #endif 67 | #endif 68 | #endif 69 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/ViewTypes/TextEditor.swift: -------------------------------------------------------------------------------- 1 | #if !os(watchOS) 2 | import SwiftUI 3 | 4 | /// An abstract representation of the `TextEditor` type in SwiftUI. 5 | /// 6 | /// ### iOS 7 | /// 8 | /// ```swift 9 | /// struct ContentView: View { 10 | /// @State var text = "Lorem ipsum" 11 | /// 12 | /// var body: some View { 13 | /// TextEditor(text: $text) 14 | /// .introspect(.textEditor, on: .iOS(.v14, .v15, .v16, .v17)) { 15 | /// print(type(of: $0)) // UITextView 16 | /// } 17 | /// } 18 | /// } 19 | /// ``` 20 | /// 21 | /// ### tvOS 22 | /// 23 | /// Not available. 24 | /// 25 | /// ### macOS 26 | /// 27 | /// ```swift 28 | /// struct ContentView: View { 29 | /// @State var text = "Lorem ipsum" 30 | /// 31 | /// var body: some View { 32 | /// TextEditor(text: $text) 33 | /// .introspect(.textEditor, on: .macOS(.v11, .v12, .v13, .v14)) { 34 | /// print(type(of: $0)) // NSTextView 35 | /// } 36 | /// } 37 | /// } 38 | /// ``` 39 | /// 40 | /// ### visionOS 41 | /// 42 | /// ```swift 43 | /// struct ContentView: View { 44 | /// @State var text = "Lorem ipsum" 45 | /// 46 | /// var body: some View { 47 | /// TextEditor(text: $text) 48 | /// .introspect(.textEditor, on: .visionOS(.v1)) { 49 | /// print(type(of: $0)) // UITextView 50 | /// } 51 | /// } 52 | /// } 53 | /// ``` 54 | public struct TextEditorType: IntrospectableViewType {} 55 | 56 | #if !os(tvOS) 57 | extension IntrospectableViewType where Self == TextEditorType { 58 | public static var textEditor: Self { .init() } 59 | } 60 | 61 | #if canImport(UIKit) 62 | extension iOSViewVersion { 63 | @available(*, unavailable, message: "TextEditor isn't available on iOS 13") 64 | public static let v13 = Self.unavailable() 65 | public static let v14 = Self(for: .v14) 66 | public static let v15 = Self(for: .v15) 67 | public static let v16 = Self(for: .v16) 68 | public static let v17 = Self(for: .v17) 69 | } 70 | 71 | extension visionOSViewVersion { 72 | public static let v1 = Self(for: .v1) 73 | } 74 | #elseif canImport(AppKit) 75 | extension macOSViewVersion { 76 | @available(*, unavailable, message: "TextEditor isn't available on macOS 10.15") 77 | public static let v10_15 = Self.unavailable() 78 | public static let v11 = Self(for: .v11) 79 | public static let v12 = Self(for: .v12) 80 | public static let v13 = Self(for: .v13) 81 | public static let v14 = Self(for: .v14) 82 | } 83 | #endif 84 | #endif 85 | #endif 86 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/ViewTypes/Toggle.swift: -------------------------------------------------------------------------------- 1 | #if !os(watchOS) 2 | import SwiftUI 3 | 4 | /// An abstract representation of the `Toggle` type in SwiftUI. 5 | /// 6 | /// ### iOS 7 | /// 8 | /// ```swift 9 | /// struct ContentView: View { 10 | /// @State var isOn = false 11 | /// 12 | /// var body: some View { 13 | /// Toggle("Toggle", isOn: $isOn) 14 | /// .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { 15 | /// print(type(of: $0)) // UISwitch 16 | /// } 17 | /// } 18 | /// } 19 | /// ``` 20 | /// 21 | /// ### tvOS 22 | /// 23 | /// Not available. 24 | /// 25 | /// ### macOS 26 | /// 27 | /// ```swift 28 | /// struct ContentView: View { 29 | /// @State var isOn = false 30 | /// 31 | /// var body: some View { 32 | /// Toggle("Toggle", isOn: $isOn) 33 | /// .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { 34 | /// print(type(of: $0)) // NSButton 35 | /// } 36 | /// } 37 | /// } 38 | /// ``` 39 | /// 40 | /// ### visionOS 41 | /// 42 | /// Not available. 43 | public struct ToggleType: IntrospectableViewType {} 44 | 45 | #if !os(tvOS) && !os(visionOS) 46 | extension IntrospectableViewType where Self == ToggleType { 47 | public static var toggle: Self { .init() } 48 | } 49 | 50 | #if canImport(UIKit) 51 | extension iOSViewVersion { 52 | public static let v13 = Self(for: .v13) 53 | public static let v14 = Self(for: .v14) 54 | public static let v15 = Self(for: .v15) 55 | public static let v16 = Self(for: .v16) 56 | public static let v17 = Self(for: .v17) 57 | } 58 | #elseif canImport(AppKit) 59 | extension macOSViewVersion { 60 | public static let v10_15 = Self(for: .v10_15) 61 | public static let v11 = Self(for: .v11) 62 | public static let v12 = Self(for: .v12) 63 | public static let v13 = Self(for: .v13) 64 | public static let v14 = Self(for: .v14) 65 | } 66 | #endif 67 | #endif 68 | #endif 69 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/ViewTypes/ToggleWithButtonStyle.swift: -------------------------------------------------------------------------------- 1 | #if !os(watchOS) 2 | import SwiftUI 3 | 4 | /// An abstract representation of the `Toggle` type in SwiftUI, with `.button` style. 5 | /// 6 | /// ### iOS 7 | /// 8 | /// Not available. 9 | /// 10 | /// ### tvOS 11 | /// 12 | /// Not available. 13 | /// 14 | /// ### macOS 15 | /// 16 | /// ```swift 17 | /// struct ContentView: View { 18 | /// @State var isOn = false 19 | /// 20 | /// var body: some View { 21 | /// Toggle("Toggle", isOn: $isOn) 22 | /// .toggleStyle(.button) 23 | /// .introspect(.toggle(style: .button), on: .macOS(.v12, .v13, .v14)) { 24 | /// print(type(of: $0)) // NSButton 25 | /// } 26 | /// } 27 | /// } 28 | /// ``` 29 | /// 30 | /// ### visionOS 31 | /// 32 | /// Not available. 33 | public struct ToggleWithButtonStyleType: IntrospectableViewType { 34 | public enum Style { 35 | case button 36 | } 37 | } 38 | 39 | #if !os(iOS) && !os(tvOS) && !os(visionOS) 40 | extension IntrospectableViewType where Self == ToggleWithButtonStyleType { 41 | public static func toggle(style: Self.Style) -> Self { .init() } 42 | } 43 | 44 | #if canImport(AppKit) && !targetEnvironment(macCatalyst) 45 | extension macOSViewVersion { 46 | @available(*, unavailable, message: ".toggleStyle(.button) isn't available on macOS 10.15") 47 | public static let v10_15 = Self.unavailable() 48 | @available(*, unavailable, message: ".toggleStyle(.button) isn't available on macOS 11") 49 | public static let v11 = Self.unavailable() 50 | public static let v12 = Self(for: .v12) 51 | public static let v13 = Self(for: .v13) 52 | public static let v14 = Self(for: .v14) 53 | } 54 | #endif 55 | #endif 56 | #endif 57 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/ViewTypes/ToggleWithCheckboxStyle.swift: -------------------------------------------------------------------------------- 1 | #if !os(watchOS) 2 | import SwiftUI 3 | 4 | /// An abstract representation of the `Toggle` type in SwiftUI, with `.checkbox` style. 5 | /// 6 | /// ### iOS 7 | /// 8 | /// Not available. 9 | /// 10 | /// ### tvOS 11 | /// 12 | /// Not available. 13 | /// 14 | /// ### macOS 15 | /// 16 | /// ```swift 17 | /// struct ContentView: View { 18 | /// @State var isOn = false 19 | /// 20 | /// var body: some View { 21 | /// Toggle("Checkbox", isOn: $isOn) 22 | /// .toggleStyle(.checkbox) 23 | /// .introspect(.toggle(style: .checkbox), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { 24 | /// print(type(of: $0)) // NSButton 25 | /// } 26 | /// } 27 | /// } 28 | /// ``` 29 | /// 30 | /// ### visionOS 31 | /// 32 | /// Not available. 33 | public struct ToggleWithCheckboxStyleType: IntrospectableViewType { 34 | public enum Style { 35 | case checkbox 36 | } 37 | } 38 | 39 | #if !os(iOS) && !os(tvOS) && !os(visionOS) 40 | extension IntrospectableViewType where Self == ToggleWithCheckboxStyleType { 41 | public static func toggle(style: Self.Style) -> Self { .init() } 42 | } 43 | 44 | #if canImport(AppKit) && !targetEnvironment(macCatalyst) 45 | extension macOSViewVersion { 46 | public static let v10_15 = Self(for: .v10_15) 47 | public static let v11 = Self(for: .v11) 48 | public static let v12 = Self(for: .v12) 49 | public static let v13 = Self(for: .v13) 50 | public static let v14 = Self(for: .v14) 51 | } 52 | #endif 53 | #endif 54 | #endif 55 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/ViewTypes/ToggleWithSwitchStyle.swift: -------------------------------------------------------------------------------- 1 | #if !os(watchOS) 2 | import SwiftUI 3 | 4 | /// An abstract representation of the `Toggle` type in SwiftUI, with `.switch` style. 5 | /// 6 | /// ### iOS 7 | /// 8 | /// ```swift 9 | /// struct ContentView: View { 10 | /// @State var isOn = false 11 | /// 12 | /// var body: some View { 13 | /// Toggle("Switch", isOn: $isOn) 14 | /// .toggleStyle(.switch) 15 | /// .introspect(.toggle(style: .switch), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { 16 | /// print(type(of: $0)) // UISwitch 17 | /// } 18 | /// } 19 | /// } 20 | /// ``` 21 | /// 22 | /// ### tvOS 23 | /// 24 | /// Not available. 25 | /// 26 | /// ### macOS 27 | /// 28 | /// ```swift 29 | /// struct ContentView: View { 30 | /// @State var isOn = false 31 | /// 32 | /// var body: some View { 33 | /// Toggle("Switch", isOn: $isOn) 34 | /// .toggleStyle(.switch) 35 | /// .introspect(.toggle(style: .switch), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { 36 | /// print(type(of: $0)) // NSSwitch 37 | /// } 38 | /// } 39 | /// } 40 | /// ``` 41 | /// 42 | /// ### visionOS 43 | /// 44 | /// Not available. 45 | public struct ToggleWithSwitchStyleType: IntrospectableViewType { 46 | public enum Style { 47 | case `switch` 48 | } 49 | } 50 | 51 | #if !os(tvOS) && !os(visionOS) 52 | extension IntrospectableViewType where Self == ToggleWithSwitchStyleType { 53 | public static func toggle(style: Self.Style) -> Self { .init() } 54 | } 55 | 56 | #if canImport(UIKit) 57 | extension iOSViewVersion { 58 | public static let v13 = Self(for: .v13) 59 | public static let v14 = Self(for: .v14) 60 | public static let v15 = Self(for: .v15) 61 | public static let v16 = Self(for: .v16) 62 | public static let v17 = Self(for: .v17) 63 | } 64 | #elseif canImport(AppKit) 65 | extension macOSViewVersion { 66 | public static let v10_15 = Self(for: .v10_15) 67 | public static let v11 = Self(for: .v11) 68 | public static let v12 = Self(for: .v12) 69 | public static let v13 = Self(for: .v13) 70 | public static let v14 = Self(for: .v14) 71 | } 72 | #endif 73 | #endif 74 | #endif 75 | -------------------------------------------------------------------------------- /Pods/SwiftUIIntrospect/Sources/Weak.swift: -------------------------------------------------------------------------------- 1 | @_spi(Advanced) 2 | @propertyWrapper 3 | public final class Weak { 4 | private weak var _wrappedValue: T? 5 | 6 | public var wrappedValue: T? { 7 | get { _wrappedValue } 8 | set { _wrappedValue = newValue } 9 | } 10 | 11 | public init(wrappedValue: T? = nil) { 12 | self._wrappedValue = wrappedValue 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | ${PODS_DEVELOPMENT_LANGUAGE} 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 5.8.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Alamofire : NSObject 3 | @end 4 | @implementation PodsDummy_Alamofire 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double AlamofireVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char AlamofireVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CODE_SIGN_IDENTITY = 3 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Alamofire 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 6 | OTHER_LDFLAGS = $(inherited) -framework "CFNetwork" 7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} 11 | PODS_ROOT = ${SRCROOT} 12 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Alamofire 13 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 14 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 15 | SKIP_INSTALL = YES 16 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire.modulemap: -------------------------------------------------------------------------------- 1 | framework module Alamofire { 2 | umbrella header "Alamofire-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire.release.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CODE_SIGN_IDENTITY = 3 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Alamofire 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 6 | OTHER_LDFLAGS = $(inherited) -framework "CFNetwork" 7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} 11 | PODS_ROOT = ${SRCROOT} 12 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Alamofire 13 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 14 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 15 | SKIP_INSTALL = YES 16 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ollamaGUI/Pods-ollamaGUI-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | ${PODS_DEVELOPMENT_LANGUAGE} 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ollamaGUI/Pods-ollamaGUI-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_ollamaGUI : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_ollamaGUI 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ollamaGUI/Pods-ollamaGUI-frameworks-Debug-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-ollamaGUI/Pods-ollamaGUI-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework 3 | ${BUILT_PRODUCTS_DIR}/SwiftSoup/SwiftSoup.framework 4 | ${BUILT_PRODUCTS_DIR}/SwiftUIIntrospect/SwiftUIIntrospect.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ollamaGUI/Pods-ollamaGUI-frameworks-Debug-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework 2 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftSoup.framework 3 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftUIIntrospect.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ollamaGUI/Pods-ollamaGUI-frameworks-Release-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-ollamaGUI/Pods-ollamaGUI-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework 3 | ${BUILT_PRODUCTS_DIR}/SwiftSoup/SwiftSoup.framework 4 | ${BUILT_PRODUCTS_DIR}/SwiftUIIntrospect/SwiftUIIntrospect.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ollamaGUI/Pods-ollamaGUI-frameworks-Release-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework 2 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftSoup.framework 3 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftUIIntrospect.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ollamaGUI/Pods-ollamaGUI-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_ollamaGUIVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_ollamaGUIVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ollamaGUI/Pods-ollamaGUI.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftSoup" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftUIIntrospect" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftSoup/SwiftSoup.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftUIIntrospect/SwiftUIIntrospect.framework/Headers" 6 | LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/../Frameworks' '@loader_path/Frameworks' "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 7 | LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 8 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" -framework "SwiftSoup" -framework "SwiftUIIntrospect" 9 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 10 | PODS_BUILD_DIR = ${BUILD_DIR} 11 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 12 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 13 | PODS_ROOT = ${SRCROOT}/Pods 14 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 16 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ollamaGUI/Pods-ollamaGUI.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_ollamaGUI { 2 | umbrella header "Pods-ollamaGUI-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ollamaGUI/Pods-ollamaGUI.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftSoup" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftUIIntrospect" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftSoup/SwiftSoup.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftUIIntrospect/SwiftUIIntrospect.framework/Headers" 6 | LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/../Frameworks' '@loader_path/Frameworks' "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 7 | LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 8 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "CFNetwork" -framework "SwiftSoup" -framework "SwiftUIIntrospect" 9 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 10 | PODS_BUILD_DIR = ${BUILD_DIR} 11 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 12 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 13 | PODS_ROOT = ${SRCROOT}/Pods 14 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 16 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SwiftUIIntrospect/SwiftUIIntrospect-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | ${PODS_DEVELOPMENT_LANGUAGE} 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.1.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SwiftUIIntrospect/SwiftUIIntrospect-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_SwiftUIIntrospect : NSObject 3 | @end 4 | @implementation PodsDummy_SwiftUIIntrospect 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SwiftUIIntrospect/SwiftUIIntrospect-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SwiftUIIntrospect/SwiftUIIntrospect-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double SwiftUIIntrospectVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char SwiftUIIntrospectVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SwiftUIIntrospect/SwiftUIIntrospect.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CODE_SIGN_IDENTITY = 3 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftUIIntrospect 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 6 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} 10 | PODS_ROOT = ${SRCROOT} 11 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftUIIntrospect 12 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 13 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 14 | SKIP_INSTALL = YES 15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 16 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SwiftUIIntrospect/SwiftUIIntrospect.modulemap: -------------------------------------------------------------------------------- 1 | framework module SwiftUIIntrospect { 2 | umbrella header "SwiftUIIntrospect-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SwiftUIIntrospect/SwiftUIIntrospect.release.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CODE_SIGN_IDENTITY = 3 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftUIIntrospect 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 6 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} 10 | PODS_ROOT = ${SRCROOT} 11 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftUIIntrospect 12 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 13 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 14 | SKIP_INSTALL = YES 15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OllamaGUI by SwiftUI 2 | 3 | 4 |

5 | 6 |

7 | 8 | 9 | https://github.com/enoch1118/ollamaGUI/assets/54224095/d056434a-f27e-41a9-940b-be7053f6920c 10 | 11 | ### Now can test langchain in ollama GUI 12 |

13 | 14 |

15 | 16 | This feature is completely native and does not require an API key. 17 | It's a bit slow so far, but it does provide per-website storage, so once created, website embeds can be reused. 18 | Since it's still in preview, you can't use it except for the window of the feature. 19 | 20 | 21 | 22 | ### Introduction to Ollamagui 23 | ollama is a lightweight, extensible framework that lets you run powerful LLMs like Llama 2, Code Llama, and others on your own computer. This means you don't need to rely on cloud-based services or have specific hardware requirements. 24 | 25 | OllamaGUI: A user interface (GUI) application built for macOS using SwiftUI framework, help you to use ollama model easily 26 | 27 | ### Installation 28 | 29 | ##### Requirements 30 | * need llama2 model https://ai.meta.com/llama/ 31 | * ensure ollama server is running https://ollama.ai/ 32 | 33 | ##### Clone the GitHub repository: 34 | > ```bash 35 | >git clone https://github.com/enoch1118/ollamaGUI.git 36 | >``` 37 | 38 | ##### Open the project in Xcode: 39 | Open Xcode. 40 | Go to "File" -> "Open". 41 | Locate the ollamagui.xcworkspace file within the cloned repository and open it. 42 | 43 | ##### Install dependencies: 44 | 45 | If you haven't already, install CocoaPods Open your terminal and run 46 | ``` 47 | sudo gem install cocoapods. 48 | ``` 49 | In the terminal, navigate to the root of the OllamaGui project directory 50 | ``` 51 | pod install 52 | ``` 53 | to install required dependencies. 54 | 55 | 56 | ### Dependencies 57 | 58 | 1. swift-markdown-ui 59 | https://github.com/gonzalezreal/swift-markdown-ui 60 | 2. SwiftUIIntrospect 61 | https://github.com/siteline/swiftui-introspect 62 | 3. Alamofire 63 | https://github.com/Alamofire/Alamofire 64 | 65 | 66 | --- langchain 67 | 68 | 4. USearch 69 | https://github.com/unum-cloud/usearch -------------------------------------------------------------------------------- /image/1.1v.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enoch1118/ollamaGUI/b094a09cff130c60e9c14bf1dcbf0bff1e4020ca/image/1.1v.png -------------------------------------------------------------------------------- /image/gif.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enoch1118/ollamaGUI/b094a09cff130c60e9c14bf1dcbf0bff1e4020ca/image/gif.gif -------------------------------------------------------------------------------- /image/langchain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enoch1118/ollamaGUI/b094a09cff130c60e9c14bf1dcbf0bff1e4020ca/image/langchain.png -------------------------------------------------------------------------------- /image/png.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enoch1118/ollamaGUI/b094a09cff130c60e9c14bf1dcbf0bff1e4020ca/image/png.png -------------------------------------------------------------------------------- /image/png2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enoch1118/ollamaGUI/b094a09cff130c60e9c14bf1dcbf0bff1e4020ca/image/png2.png -------------------------------------------------------------------------------- /image/png3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enoch1118/ollamaGUI/b094a09cff130c60e9c14bf1dcbf0bff1e4020ca/image/png3.png -------------------------------------------------------------------------------- /ollamaGUI.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ollamaGUI.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ollamaGUI.xcodeproj/xcuserdata/baesanghwi.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ollamaGUI.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 3 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | AC842E342B4D16F700F9EC3F 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ollamaGUI.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ollamaGUI.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ollamaGUI.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "originHash" : "02f0af96540e313ebd0891a4fdfdda56927deee2522081f4e6b08a023c69747a", 3 | "pins" : [ 4 | { 5 | "identity" : "networkimage", 6 | "kind" : "remoteSourceControl", 7 | "location" : "https://github.com/gonzalezreal/NetworkImage", 8 | "state" : { 9 | "revision" : "7aff8d1b31148d32c5933d75557d42f6323ee3d1", 10 | "version" : "6.0.0" 11 | } 12 | }, 13 | { 14 | "identity" : "swift-markdown-ui", 15 | "kind" : "remoteSourceControl", 16 | "location" : "https://github.com/gonzalezreal/swift-markdown-ui", 17 | "state" : { 18 | "revision" : "ae799d015a5374708f7b4c85f3294c05f2a564e2", 19 | "version" : "2.3.0" 20 | } 21 | }, 22 | { 23 | "identity" : "usearch", 24 | "kind" : "remoteSourceControl", 25 | "location" : "https://github.com/unum-cloud/usearch.git", 26 | "state" : { 27 | "revision" : "f79d8180122c717203b74f7a7473964c413cb5c1", 28 | "version" : "2.9.2" 29 | } 30 | } 31 | ], 32 | "version" : 3 33 | } 34 | -------------------------------------------------------------------------------- /ollamaGUI/V2/Core/Error/MuseError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/12/24. 6 | // 7 | 8 | import Foundation 9 | 10 | enum MuseError: Error { 11 | case getDocumentError(url: String) 12 | } 13 | 14 | extension MuseError { 15 | var localizedDescription: String { 16 | switch self { 17 | case let .getDocumentError(url): 18 | return "get document from \(url) failed" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ollamaGUI/V2/Core/Protocol/BaseBloc.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseBLoc.swift 3 | // ollamaGUI 4 | // 5 | // Created by 배상휘 on 3/12/24. 6 | // 7 | 8 | import Combine 9 | import Foundation 10 | 11 | /// is Event is State 12 | class BaseBloc where S: BaseState { 13 | var eventListener: PassthroughSubject 14 | var stateSubject: CurrentValueSubject 15 | var cancel: Set 16 | 17 | init() { 18 | stateSubject = .init(.initState) 19 | eventListener = .init() 20 | cancel = Set() 21 | } 22 | 23 | func _ignite() { 24 | eventListener.sink(receiveValue: _registerEvent) 25 | .store(in: &cancel) 26 | } 27 | 28 | func _registerEvent(event: E) { 29 | if showLog { 30 | print(String(describing: event)) 31 | } 32 | } 33 | 34 | func addEvent(event: E) { 35 | eventListener.send(event) 36 | } 37 | 38 | func emit(state: S) { 39 | stateSubject.send(state) 40 | } 41 | 42 | var showLog: Bool { 43 | true 44 | } 45 | } 46 | 47 | protocol BaseState { 48 | static var initState: Self { get } 49 | } 50 | --------------------------------------------------------------------------------