├── .bundle └── config ├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .prettierrc ├── .vscode └── settings.json ├── Gemfile ├── Gemfile.lock ├── Header.jpg ├── LICENSE ├── README.md ├── app.json ├── babel.config.js ├── bun.lockb ├── fastlane ├── Appfile ├── Fastfile └── README.md ├── index.js ├── macos ├── .gitignore ├── .xcode.env ├── Podfile ├── Podfile.lock ├── PrivacyInfo.xcprivacy ├── sol-macOS │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── icon_128@1x.png │ │ │ ├── icon_128@2x.png │ │ │ ├── icon_16@1x.png │ │ │ ├── icon_16@2x.png │ │ │ ├── icon_256@1x.png │ │ │ ├── icon_256@2x.png │ │ │ ├── icon_32@1x.png │ │ │ ├── icon_32@2x.png │ │ │ ├── icon_512@1x.png │ │ │ └── icon_512@2x.png │ │ ├── Background.colorset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ └── Main.storyboard │ ├── Info.plist │ ├── extensions │ │ ├── CAGradiendLayer.swift │ │ ├── CGImage.swift │ │ ├── FileManager.extension.swift │ │ ├── NSBezierPath.extension.swift │ │ ├── NSColor.extension.swift │ │ ├── NSImage.swift │ │ └── Sequence.extension.swift │ ├── fonts │ │ └── JetBrainsMono.ttf │ ├── lib │ │ ├── AccessibilityElement.swift │ │ ├── AppleScriptHelper.swift │ │ ├── ApplicationSearcher.swift │ │ ├── BookmarkHelper.swift │ │ ├── CalendarHelper.h │ │ ├── CalendarHelper.mm │ │ ├── ClipboardHelper.swift │ │ ├── DarkMode.swift │ │ ├── DoNotDisturb.swift │ │ ├── FS.swift │ │ ├── FileConstants.swift │ │ ├── FileSearch.h │ │ ├── FileSearch.mm │ │ ├── ITunesApplicationApplication.h │ │ ├── JSIBindings.hpp │ │ ├── JSIBindings.mm │ │ ├── JSIUtils.h │ │ ├── JSIUtils.mm │ │ ├── KeychainAccess.swift │ │ ├── MediaHelper.swift │ │ ├── MediaKeyForwarder.h │ │ ├── MediaKeyForwarder.m │ │ ├── MetadataExtractorError.swift │ │ ├── MouseUtils.swift │ │ ├── NSString+Score.h │ │ ├── NSString+Score.m │ │ ├── NotchHelper.swift │ │ ├── QRGenerator.swift │ │ ├── ScreenDetector.swift │ │ ├── ShellHelper.swift │ │ ├── SolEmitter.swift │ │ ├── SolMacros.h │ │ ├── SolNative.mm │ │ ├── SolNative.swift │ │ ├── SpotifyApplication.h │ │ ├── WindowManager.swift │ │ ├── processes.h │ │ └── processes.mm │ ├── managers │ │ ├── HotKeyManager.swift │ │ ├── PanelManager.swift │ │ ├── StatusBarItemManager.swift │ │ └── ToastManager.swift │ ├── sol-macOS-Bridging-Header.h │ ├── sol-macOS.entitlements │ ├── sol.entitlements │ └── views │ │ ├── BlurView.m │ │ ├── BlurView.swift │ │ ├── FileIcon.m │ │ ├── FileIcon.swift │ │ ├── GradientView.m │ │ ├── GradientView.swift │ │ ├── Overlay.swift │ │ ├── Panel.swift │ │ ├── Toast.swift │ │ └── ToastView.swift ├── sol.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── xcshareddata │ │ │ └── swiftpm │ │ │ └── Package.resolved │ └── xcshareddata │ │ └── xcschemes │ │ ├── debug.xcscheme │ │ └── release.xcscheme └── sol.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ ├── IDEWorkspaceChecks.plist │ ├── WorkspaceSettings.xcsettings │ └── swiftpm │ └── Package.resolved ├── metro.config.js ├── mise.toml ├── package.json ├── releases ├── appcast.xml └── old_updates │ └── Sol406-405.delta ├── scripts ├── appcast.sh └── bump-version.sh ├── src ├── app.tsx ├── assets.ts ├── assets │ ├── Bluetooth.png │ ├── Cake.png │ ├── Check.png │ ├── CheckCircleIcon.png │ ├── ChevronDown.png │ ├── ChevronLeft.png │ ├── ChevronUp.png │ ├── DarkModeIcon.png │ ├── DocumentIcon.png │ ├── Giphy.png │ ├── GoogleMapsIcon.png │ ├── LockIcon.png │ ├── SearchIcon.png │ ├── SettingsIcon.png │ ├── SleepIcon.png │ ├── SolBlackSmall.png │ ├── SolWhiteSmall.png │ ├── accessibility.png │ ├── accounts.png │ ├── airdrop.png │ ├── arrowLeftBlack.png │ ├── arrowLeftWhite.png │ ├── battery.png │ ├── brave.png │ ├── chrome.png │ ├── close.png │ ├── customIcons │ │ ├── Africa.png │ │ ├── Airplane.png │ │ ├── Alert.png │ │ ├── Anchor.png │ │ ├── Android.png │ │ ├── Apple.png │ │ ├── Asia.png │ │ ├── Australia.png │ │ ├── Bank.png │ │ ├── BarChart.png │ │ ├── Basket.png │ │ ├── Battery.png │ │ ├── Bike.png │ │ ├── Bitcoin.png │ │ ├── Bolt.png │ │ ├── Book.png │ │ ├── Bookmark.png │ │ ├── Briefcase.png │ │ ├── Brush.png │ │ ├── Bucket.png │ │ ├── Bug.png │ │ ├── Calendar.png │ │ ├── Camera.png │ │ ├── Car.png │ │ ├── Cart.png │ │ ├── Chart.png │ │ ├── Chat.png │ │ ├── Chip.png │ │ ├── Clock.png │ │ ├── Cloud.png │ │ ├── Compass.png │ │ ├── Computer.png │ │ ├── Cone.png │ │ ├── CreditCard.png │ │ ├── Cross.png │ │ ├── Crown.png │ │ ├── Cube.png │ │ ├── Dashboard.png │ │ ├── Database.png │ │ ├── DesignTools.png │ │ ├── Dna.png │ │ ├── Dollar.png │ │ ├── Education.png │ │ ├── Ethereum.png │ │ ├── Euro.png │ │ ├── Europe.png │ │ ├── Face.png │ │ ├── FaceFlatSmile.png │ │ ├── FaceHeartEyes.png │ │ ├── FaceMask.png │ │ ├── FaceMonocle.png │ │ ├── FaceStarEyes.png │ │ ├── FaceSunglasses.png │ │ ├── FaceSurprise.png │ │ ├── FaceTired.png │ │ ├── FaceTongue.png │ │ ├── Favorite.png │ │ ├── Feather.png │ │ ├── Fire.png │ │ ├── Flower.png │ │ ├── Folder.png │ │ ├── Gears.png │ │ ├── Github.png │ │ ├── Gitlab.png │ │ ├── Heart.png │ │ ├── Home.png │ │ ├── Image.png │ │ ├── Inbox.png │ │ ├── Joystick.png │ │ ├── Label.png │ │ ├── Leaf.png │ │ ├── LightBulb.png │ │ ├── Link.png │ │ ├── Lock.png │ │ ├── Megaphone.png │ │ ├── MobilePhone.png │ │ ├── Mountain.png │ │ ├── NorthAmerica.png │ │ ├── Page.png │ │ ├── Phone.png │ │ ├── PieChart.png │ │ ├── Pin.png │ │ ├── Present.png │ │ ├── Project.png │ │ ├── Radar.png │ │ ├── Recycle.png │ │ ├── Robot.png │ │ ├── Rocket.png │ │ ├── Search.png │ │ ├── Send.png │ │ ├── Server.png │ │ ├── Shield.png │ │ ├── Ship.png │ │ ├── Shop.png │ │ ├── Sign.png │ │ ├── Skull.png │ │ ├── Sound.png │ │ ├── SouthAmerica.png │ │ ├── Subscribe.png │ │ ├── Sun.png │ │ ├── Tablet.png │ │ ├── Team.png │ │ ├── TeeShirt.png │ │ ├── Terminal.png │ │ ├── ThumbsDown.png │ │ ├── ThumbsUp.png │ │ ├── Trash.png │ │ ├── Tree.png │ │ ├── Umbrella.png │ │ ├── UnhappyFace.png │ │ ├── Users.png │ │ ├── Video.png │ │ ├── Watch.png │ │ └── World.png │ ├── display.png │ ├── google.png │ ├── google_translate.png │ ├── inbox.png │ ├── logoMinimal.png │ ├── logoMinimalWhite.png │ ├── macosSettings.png │ ├── network.png │ ├── osp.png │ ├── safari.png │ ├── shortcuts.png │ ├── siri.png │ ├── smallLogo.png │ ├── sound.png │ ├── star.png │ ├── starFilled.png │ ├── touch.png │ ├── translate.png │ ├── users.png │ ├── wallpaper.png │ └── wifi.png ├── colors.js ├── components │ ├── BackButton.tsx │ ├── BlurView.tsx │ ├── Dropdown.tsx │ ├── Fade.tsx │ ├── FileIcon.tsx │ ├── FullCalendar.tsx │ ├── GradientView.tsx │ ├── Input.tsx │ ├── Key.tsx │ ├── LoadingBar.tsx │ ├── MainInput.tsx │ ├── MyRadioButton.tsx │ ├── MySwitch.tsx │ ├── PermissionsBar.tsx │ ├── SelectableButton.tsx │ ├── SolButton.tsx │ └── SystemPreferencesIcon.tsx ├── config.ts ├── containers │ ├── index.ts │ └── root.container.tsx ├── global.css ├── globals.d.ts ├── hooks │ ├── index.ts │ ├── useBoolean.ts │ └── useFullSize.ts ├── lib │ ├── SolNative.ts │ ├── calendar.ts │ ├── constants.ts │ ├── emojis.ts │ ├── github.ts │ ├── languages.ts │ ├── shorcuts.tsx │ ├── translator.ts │ ├── various.ts │ └── weather.ts ├── mytailwind.ts ├── store.ts ├── stores │ ├── calendar.store.tsx │ ├── clipboard.store.tsx │ ├── emoji.store.tsx │ ├── items.tsx │ ├── keystroke.store.ts │ ├── processes.store.tsx │ ├── storage.ts │ ├── systemPreferences.tsx │ └── ui.store.tsx └── widgets │ ├── calendar.widget.tsx │ ├── clipboard.widget.tsx │ ├── createItem.widget.tsx │ ├── emojis.widget.tsx │ ├── fileSearch.widget.tsx │ ├── onboarding.widget.tsx │ ├── processes.widget.tsx │ ├── scratchpad.widget.tsx │ ├── search.widget.tsx │ ├── settings.widget.tsx │ ├── settings │ ├── about.tsx │ ├── general.tsx │ ├── shortcuts.tsx │ ├── sidebar.tsx │ └── translate.tsx │ └── translation.widget.tsx ├── tailwind.config.js └── tsconfig.json /.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_PATH: "vendor/bundle" 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [ospfranco] 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a report to help us improve sol 4 | title: '[BUG] ' 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | 13 | 14 | ## Environment Information 15 | 16 | 17 | 18 | **macOS Version:** 19 | 20 | 21 | 22 | **sol Version:** 23 | 24 | 25 | 26 | ## Issue Description 27 | 28 | 29 | 30 | ## Reproduction Steps 31 | 32 | 33 | 34 | 1. 35 | 2. 36 | 3. 37 | 38 | ## Expected Behavior 39 | 40 | 41 | 42 | ## Actual Behavior 43 | 44 | 45 | 46 | ## Screenshots/Logs 47 | 48 | 49 | 50 | ## Additional Context 51 | 52 | 53 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest an idea or enhancement for sol 4 | title: '[FEATURE] ' 5 | labels: enhancement 6 | assignees: '' 7 | --- 8 | 9 | 13 | 14 | ## Feature Description 15 | 16 | 17 | 18 | ## Problem Statement 19 | 20 | 21 | 22 | ## Proposed Solution 23 | 24 | 25 | 26 | ## Use Cases 27 | 28 | 29 | 30 | 1. 31 | 2. 32 | 3. 33 | 34 | ## Alternatives Considered 35 | 36 | 37 | 38 | ## Additional Context 39 | 40 | 41 | 42 | ## Impact 43 | 44 | 45 | 46 | ## Priority 47 | 48 | 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | ios/.xcode.env.local 24 | 25 | # Windows 26 | msbuild.binlog 27 | 28 | # Android/IntelliJ 29 | # 30 | build/ 31 | .idea 32 | .gradle 33 | local.properties 34 | *.iml 35 | 36 | .cxx/ 37 | *.keystore 38 | !debug.keystore 39 | 40 | # node.js 41 | # 42 | node_modules/ 43 | npm-debug.log 44 | yarn-error.log 45 | 46 | # fastlane 47 | # 48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 49 | # screenshots whenever they are needed. 50 | # For more information about the recommended setup visit: 51 | # https://docs.fastlane.tools/best-practices/source-control/ 52 | 53 | */fastlane/report.xml 54 | */fastlane/Preview.html 55 | */fastlane/screenshots 56 | 57 | # Bundle artifact 58 | *.jsbundle 59 | 60 | # CocoaPods 61 | /ios/Pods/ 62 | env.json 63 | 64 | # Fastlane 65 | /fastlane/report.xml 66 | 67 | # Built binaries 68 | **/Sol.app 69 | **/Sol.app.zip 70 | 71 | vendor 72 | 73 | # Temporary files created by Metro to check the health of the file watcher 74 | .metro-health-check* 75 | 76 | src/env.ts 77 | Sol.app.zip 78 | Sol.app copy.zip 79 | sentry.properties 80 | releases/*.zip -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "bracketSpacing": false, 4 | "jsxBracketSameLine": true, 5 | "singleQuote": true, 6 | "trailingComma": "all", 7 | "arrowParens": "avoid" 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "workbench.colorCustomizations": { 3 | "sash.hoverBorder": "#39a7fe", 4 | "statusBar.background": "#0691fe", 5 | "statusBar.foreground": "#e7e7e7", 6 | "statusBarItem.hoverBackground": "#39a7fe", 7 | "statusBarItem.remoteBackground": "#0691fe", 8 | "statusBarItem.remoteForeground": "#e7e7e7" 9 | }, 10 | "peacock.color": "#0691fe" 11 | } 12 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "fastlane" 4 | gem "cocoapods" -------------------------------------------------------------------------------- /Header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/Header.jpg -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 Oscar Franco 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. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sol 2 | 3 | ![Header](Header.jpg) 4 | 5 |
6 |
7 | 8 | 9 | 10 |
11 | 12 | Sol is an open source app launcher, focused on ease of use and speed. It has minimal configuration and runs natively. 13 | 14 | [Visit official site](https://sol.ospfranco.com) 15 | 16 | ## Download 17 | 18 | Install via brew 19 | 20 | ``` 21 | brew install --cask sol 22 | ``` 23 | 24 | Or manually download the latest [release](https://github.com/ospfranco/sol/tree/main/releases). 25 | 26 | ## Discord 27 | 28 | Join the Discord 29 | 30 | https://discord.gg/W9XmqCQCKP 31 | 32 | ## Features 33 | 34 | - App search 35 | - Custom shortcuts 36 | - Google translate 37 | - Calendar 38 | - Show upcoming appointement in Menu Bar 39 | - Custom AppleScript commands 40 | - Custom links 41 | - Imports browser bookmarks 42 | - Window Manager 43 | - Emoji picker 44 | - Clipboard manager 45 | - Notes Scratchpad 46 | - Retrieve Wi-Fi password 47 | - Show IP address 48 | - Start a google meet 49 | - Switch OS theme 50 | - Process killer 51 | - Clear XCode Derived Data 52 | - Generate NanoID 53 | - Generate UUID 54 | - Generate lorem ipsum 55 | - Format and paste JSON 56 | - Forward media keys to Spotify/Apple Music 57 | - Blacken Menu Bar 58 | - Quickly evaluate math operations 59 | 60 | ## Contributing 61 | 62 | You need to set up your machine for macOS development with React Native. Basically you need to install: 63 | 64 | - Mise (https://mise.jdx.dev/) 65 | - Xcode 66 | - Cocoapods 67 | 68 | Follow any of the online tutorials to set up your machine for iOS/MacOS React Native development. 69 | 70 | Once you have everything installed run the following commands 71 | 72 | ```sh 73 | mise plugin add cocoapods 74 | # To enable hooks 75 | mise settings experimental=true 76 | # Will install all bun, ruby and run the installation of dependencies 77 | mise install 78 | 79 | # You can then run the app with 80 | bun macos 81 | ``` 82 | 83 | ## License 84 | 85 | MIT License 86 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sol", 3 | "displayName": "Sol" 4 | } 5 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:@react-native/babel-preset', 'nativewind/babel'], 3 | plugins: [ 4 | [ 5 | 'module-resolver', 6 | { 7 | root: ['./src'], 8 | extensions: ['.js', '.ts', '.tsx', '.ios.js', '.android.js'], 9 | }, 10 | ], 11 | ], 12 | } 13 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/bun.lockb -------------------------------------------------------------------------------- /fastlane/Appfile: -------------------------------------------------------------------------------- 1 | app_identifier("com.ospfranco.sol") 2 | apple_id("ospfranco@protonmail.com") -------------------------------------------------------------------------------- /fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | default_platform(:mac) 2 | 3 | platform :mac do 4 | 5 | lane :release do 6 | Dir.chdir("..") do 7 | sh("bun", "bump") 8 | end 9 | 10 | version = get_version_number(xcodeproj: "./macos/sol.xcodeproj") 11 | 12 | path = "/Users/osp/Developer/sol/" 13 | appFileName = path + "Sol.app" 14 | zipFileName = path + "releases/" + version + ".zip" 15 | 16 | gym( 17 | scheme: "release", 18 | configuration: "Release", 19 | clean: true, 20 | workspace: "./macos/sol.xcworkspace", 21 | output_directory: path, 22 | export_method: "developer-id" 23 | ) 24 | 25 | notarize( 26 | package: appFileName, 27 | bundle_id: "com.ospfranco.sol", 28 | username: "ospfranco@protonmail.com", 29 | asc_provider: "24CMR7378R" 30 | ) 31 | 32 | zip( 33 | path: appFileName, 34 | output_path: zipFileName, 35 | symlinks: true 36 | ) 37 | 38 | set_github_release( 39 | repository_name: "ospfranco/sol", 40 | api_token: ENV["GITHUB_API_TOKEN"], 41 | name: "v" + version, 42 | tag_name: version, 43 | description: "No release notes provided.", 44 | upload_assets: [zipFileName] 45 | ) 46 | 47 | file_url = "https://github.com/ospfranco/sol/releases/download/" + version + "/" + version + ".zip" 48 | 49 | 50 | Dir.chdir("..") do 51 | sh("bun", "appcast") 52 | 53 | sh("./scripts/appcast.sh", file_url, version) 54 | 55 | Dir.glob("#{path}releases/*.zip").each { |file| File.delete(file) } 56 | 57 | sh("git", "add", ".") 58 | sh("git", "commit", "-m", version) 59 | sh("git", "push") 60 | end 61 | 62 | sh("cp", "-R", appFileName, "/Applications/") 63 | 64 | sh("open", "/Applications/Sol.app") 65 | 66 | sh("pingme", "Sol " + version + " released") 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /fastlane/README.md: -------------------------------------------------------------------------------- 1 | fastlane documentation 2 | ---- 3 | 4 | # Installation 5 | 6 | Make sure you have the latest version of the Xcode command line tools installed: 7 | 8 | ```sh 9 | xcode-select --install 10 | ``` 11 | 12 | For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane) 13 | 14 | # Available Actions 15 | 16 | ## Mac 17 | 18 | ### mac release 19 | 20 | ```sh 21 | [bundle exec] fastlane mac release 22 | ``` 23 | 24 | 25 | 26 | ---- 27 | 28 | This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run. 29 | 30 | More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools). 31 | 32 | The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools). 33 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import {AppRegistry} from 'react-native' 2 | import 'react-native-get-random-values' 3 | import {name as appName} from './app.json' 4 | import {App} from './src/app' 5 | 6 | AppRegistry.registerComponent(appName, () => App) 7 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # CocoaPods 2 | Pods/ 3 | -------------------------------------------------------------------------------- /macos/.xcode.env: -------------------------------------------------------------------------------- 1 | export NODE_BINARY=$(command -v node) -------------------------------------------------------------------------------- /macos/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native-macos/scripts/react_native_pods' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | prepare_react_native_project! 5 | 6 | ENV['RCT_NEW_ARCH_ENABLED'] = '0' 7 | 8 | target 'macOS' do 9 | platform :macos, '11' 10 | use_native_modules! 11 | 12 | use_react_native!( 13 | :path => '../node_modules/react-native-macos', 14 | :hermes_enabled => true, 15 | :fabric_enabled => false, 16 | :app_path => "#{Pod::Config.instance.installation_root}/.." 17 | ) 18 | 19 | pod 'Sparkle' 20 | pod 'HotKey' 21 | 22 | post_install do |installer| 23 | react_native_post_install(installer) 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /macos/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyAccessedAPITypes 6 | 7 | 8 | NSPrivacyAccessedAPIType 9 | NSPrivacyAccessedAPICategoryFileTimestamp 10 | NSPrivacyAccessedAPITypeReasons 11 | 12 | C617.1 13 | 14 | 15 | 16 | NSPrivacyAccessedAPIType 17 | NSPrivacyAccessedAPICategoryUserDefaults 18 | NSPrivacyAccessedAPITypeReasons 19 | 20 | CA92.1 21 | 22 | 23 | 24 | NSPrivacyAccessedAPIType 25 | NSPrivacyAccessedAPICategorySystemBootTime 26 | NSPrivacyAccessedAPITypeReasons 27 | 28 | 35F9.1 29 | 30 | 31 | 32 | NSPrivacyCollectedDataTypes 33 | 34 | NSPrivacyTracking 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /macos/sol-macOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_16@1x.png", 5 | "idiom" : "mac", 6 | "scale" : "1x", 7 | "size" : "16x16" 8 | }, 9 | { 10 | "filename" : "icon_16@2x.png", 11 | "idiom" : "mac", 12 | "scale" : "2x", 13 | "size" : "16x16" 14 | }, 15 | { 16 | "filename" : "icon_32@1x.png", 17 | "idiom" : "mac", 18 | "scale" : "1x", 19 | "size" : "32x32" 20 | }, 21 | { 22 | "filename" : "icon_32@2x.png", 23 | "idiom" : "mac", 24 | "scale" : "2x", 25 | "size" : "32x32" 26 | }, 27 | { 28 | "filename" : "icon_128@1x.png", 29 | "idiom" : "mac", 30 | "scale" : "1x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "filename" : "icon_128@2x.png", 35 | "idiom" : "mac", 36 | "scale" : "2x", 37 | "size" : "128x128" 38 | }, 39 | { 40 | "filename" : "icon_256@1x.png", 41 | "idiom" : "mac", 42 | "scale" : "1x", 43 | "size" : "256x256" 44 | }, 45 | { 46 | "filename" : "icon_256@2x.png", 47 | "idiom" : "mac", 48 | "scale" : "2x", 49 | "size" : "256x256" 50 | }, 51 | { 52 | "filename" : "icon_512@1x.png", 53 | "idiom" : "mac", 54 | "scale" : "1x", 55 | "size" : "512x512" 56 | }, 57 | { 58 | "filename" : "icon_512@2x.png", 59 | "idiom" : "mac", 60 | "scale" : "2x", 61 | "size" : "512x512" 62 | } 63 | ], 64 | "info" : { 65 | "author" : "xcode", 66 | "version" : 1 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /macos/sol-macOS/Assets.xcassets/AppIcon.appiconset/icon_128@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/macos/sol-macOS/Assets.xcassets/AppIcon.appiconset/icon_128@1x.png -------------------------------------------------------------------------------- /macos/sol-macOS/Assets.xcassets/AppIcon.appiconset/icon_128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/macos/sol-macOS/Assets.xcassets/AppIcon.appiconset/icon_128@2x.png -------------------------------------------------------------------------------- /macos/sol-macOS/Assets.xcassets/AppIcon.appiconset/icon_16@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/macos/sol-macOS/Assets.xcassets/AppIcon.appiconset/icon_16@1x.png -------------------------------------------------------------------------------- /macos/sol-macOS/Assets.xcassets/AppIcon.appiconset/icon_16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/macos/sol-macOS/Assets.xcassets/AppIcon.appiconset/icon_16@2x.png -------------------------------------------------------------------------------- /macos/sol-macOS/Assets.xcassets/AppIcon.appiconset/icon_256@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/macos/sol-macOS/Assets.xcassets/AppIcon.appiconset/icon_256@1x.png -------------------------------------------------------------------------------- /macos/sol-macOS/Assets.xcassets/AppIcon.appiconset/icon_256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/macos/sol-macOS/Assets.xcassets/AppIcon.appiconset/icon_256@2x.png -------------------------------------------------------------------------------- /macos/sol-macOS/Assets.xcassets/AppIcon.appiconset/icon_32@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/macos/sol-macOS/Assets.xcassets/AppIcon.appiconset/icon_32@1x.png -------------------------------------------------------------------------------- /macos/sol-macOS/Assets.xcassets/AppIcon.appiconset/icon_32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/macos/sol-macOS/Assets.xcassets/AppIcon.appiconset/icon_32@2x.png -------------------------------------------------------------------------------- /macos/sol-macOS/Assets.xcassets/AppIcon.appiconset/icon_512@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/macos/sol-macOS/Assets.xcassets/AppIcon.appiconset/icon_512@1x.png -------------------------------------------------------------------------------- /macos/sol-macOS/Assets.xcassets/AppIcon.appiconset/icon_512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/macos/sol-macOS/Assets.xcassets/AppIcon.appiconset/icon_512@2x.png -------------------------------------------------------------------------------- /macos/sol-macOS/Assets.xcassets/Background.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xFF", 9 | "green" : "0xFF", 10 | "red" : "0xFF" 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" : "0x00", 27 | "green" : "0x00", 28 | "red" : "0x00" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /macos/sol-macOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /macos/sol-macOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ATSApplicationFontsPath 6 | JetBrainsMono.ttf 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleDisplayName 10 | Sol 11 | CFBundleExecutable 12 | $(EXECUTABLE_NAME) 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | $(PRODUCT_NAME) 19 | CFBundlePackageType 20 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 21 | CFBundleShortVersionString 22 | 2.1.200 23 | CFBundleVersion 24 | 414 25 | LSApplicationCategoryType 26 | public.app-category.productivity 27 | LSMinimumSystemVersion 28 | $(MACOSX_DEPLOYMENT_TARGET) 29 | LSUIElement 30 | 31 | NSAppTransportSecurity 32 | 33 | NSAllowsArbitraryLoads 34 | 35 | NSExceptionDomains 36 | 37 | localhost 38 | 39 | NSExceptionAllowsInsecureHTTPLoads 40 | 41 | 42 | 43 | 44 | NSAppleEventsUsageDescription 45 | Sol needs permision to execute apple scripts 46 | NSCalendarsFullAccessUsageDescription 47 | Sol needs permission to display your appointments 48 | NSCalendarsUsageDescription 49 | Sol needs permission to display your appointments 50 | NSLocationAlwaysAndWhenInUseUsageDescription 51 | Allow location access to show Wi-Fi password 52 | NSLocationAlwaysUsageDescription 53 | Allow location access to show Wi-Fi password 54 | NSLocationWhenInUseUsageDescription 55 | Allow location access to show Wi-Fi password 56 | NSMainStoryboardFile 57 | Main 58 | NSPrincipalClass 59 | NSApplication 60 | NSSupportsAutomaticTermination 61 | 62 | NSSupportsSuddenTermination 63 | 64 | NSSystemAdministrationUsageDescription 65 | Sol needs permissions to read bookmarks and other files 66 | SUFeedURL 67 | https://raw.githubusercontent.com/ospfranco/sol/main/releases/appcast.xml 68 | SUPublicEDKey 69 | K+wkzEuPN5g0KIQQTxQIVrZAWfMoowpia/yAopR7sMk= 70 | 71 | 72 | -------------------------------------------------------------------------------- /macos/sol-macOS/extensions/CAGradiendLayer.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | //helper extention to CAGradientLayer 4 | extension CAGradientLayer { 5 | func apply(angle : Double) { 6 | let x: Double! = angle / 360.0 7 | let a = pow(sinf(Float(2.0 * Double.pi * ((x + 0.75) / 2.0))),2.0); 8 | let b = pow(sinf(Float(2*Double.pi*((x+0.0)/2))),2); 9 | let c = pow(sinf(Float(2*Double.pi*((x+0.25)/2))),2); 10 | let d = pow(sinf(Float(2*Double.pi*((x+0.5)/2))),2); 11 | 12 | endPoint = CGPoint(x: CGFloat(c),y: CGFloat(d)) 13 | startPoint = CGPoint(x: CGFloat(a),y:CGFloat(b)) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /macos/sol-macOS/extensions/CGImage.swift: -------------------------------------------------------------------------------- 1 | import CoreGraphics 2 | 3 | extension CGImage { 4 | func crop(toSize targetSize: CGSize) -> CGImage? { 5 | let x = floor(CGFloat(self.width) / 2 - targetSize.width / 2) 6 | let y = floor(CGFloat(self.height) / 2 - targetSize.height / 2) 7 | let frame = CGRect(x: x, y: y, width: targetSize.width, height: targetSize.height) 8 | 9 | return self.cropping(to: frame) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /macos/sol-macOS/extensions/FileManager.extension.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension FileManager { 4 | func secureCopyItem(at srcURL: URL, to dstURL: URL) { 5 | do { 6 | if FileManager.default.fileExists(atPath: dstURL.path) { 7 | try FileManager.default.removeItem(at: dstURL) 8 | } 9 | 10 | try FileManager.default.copyItem(at: srcURL, to: dstURL) 11 | } catch let error { 12 | print("SOL, could not copy file \(error)") 13 | } 14 | } 15 | 16 | func secureDeleteItem(at srcURL: URL) { 17 | do { 18 | if FileManager.default.fileExists(atPath: srcURL.path) { 19 | try FileManager.default.removeItem(at: srcURL) 20 | } 21 | } catch let error { 22 | print("SOL, could not delete file \(error)") 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /macos/sol-macOS/extensions/NSBezierPath.extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSBezierPath.extension.swift 3 | // macOS 4 | // 5 | // Created by Oscar Franco on 23.04.23. 6 | // 7 | 8 | import Foundation 9 | 10 | extension NSBezierPath { 11 | 12 | public var cgPath: CGPath { 13 | let path = CGMutablePath() 14 | var points = [CGPoint](repeating: .zero, count: 3) 15 | 16 | for i in 0 ..< elementCount { 17 | let type = element(at: i, associatedPoints: &points) 18 | switch type { 19 | case .moveTo: 20 | path.move(to: points[0]) 21 | case .lineTo: 22 | path.addLine(to: points[0]) 23 | case .curveTo: 24 | path.addCurve(to: points[2], control1: points[0], control2: points[1]) 25 | case .closePath: 26 | path.closeSubpath() 27 | @unknown default: 28 | continue 29 | } 30 | } 31 | 32 | return path 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /macos/sol-macOS/extensions/NSColor.extension.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Cocoa 3 | 4 | extension NSColor { 5 | 6 | convenience init?(hex: NSString) { 7 | var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines) 8 | hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "") 9 | 10 | var rgb: UInt32 = 0 11 | 12 | var r: CGFloat = 0.0 13 | var g: CGFloat = 0.0 14 | var b: CGFloat = 0.0 15 | var a: CGFloat = 1.0 16 | 17 | let length = hexSanitized.count 18 | 19 | guard Scanner(string: hexSanitized).scanHexInt32(&rgb) else { return nil } 20 | 21 | if length == 6 { 22 | r = CGFloat((rgb & 0xFF0000) >> 16) / 255.0 23 | g = CGFloat((rgb & 0x00FF00) >> 8) / 255.0 24 | b = CGFloat(rgb & 0x0000FF) / 255.0 25 | 26 | } else if length == 8 { 27 | r = CGFloat((rgb & 0xFF000000) >> 24) / 255.0 28 | g = CGFloat((rgb & 0x00FF0000) >> 16) / 255.0 29 | b = CGFloat((rgb & 0x0000FF00) >> 8) / 255.0 30 | a = CGFloat(rgb & 0x000000FF) / 255.0 31 | 32 | } else { 33 | return nil 34 | } 35 | 36 | self.init(red: r, green: g, blue: b, alpha: a) 37 | } 38 | 39 | 40 | var hexString: String { 41 | let srgbColor = self.usingColorSpace(.sRGB)! 42 | let red = Int(round(srgbColor.redComponent * 0xFF)) 43 | let green = Int(round(srgbColor.greenComponent * 0xFF)) 44 | let blue = Int(round(srgbColor.blueComponent * 0xFF)) 45 | let hexString = NSString(format: "#%02X%02X%02X", red, green, blue) 46 | return hexString as String 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /macos/sol-macOS/extensions/Sequence.extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Sequence.extension.swift 3 | // sol-macOS 4 | // 5 | // Created by Oscar on 06.03.22. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Sequence where Element == UInt8 { 11 | var data: Data { .init(self) } 12 | var base64Decoded: Data? { Data(base64Encoded: data) } 13 | var string: String? { String(bytes: self, encoding: .utf8) } 14 | } 15 | -------------------------------------------------------------------------------- /macos/sol-macOS/fonts/JetBrainsMono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/macos/sol-macOS/fonts/JetBrainsMono.ttf -------------------------------------------------------------------------------- /macos/sol-macOS/lib/AppleScriptHelper.swift: -------------------------------------------------------------------------------- 1 | import EventKit 2 | 3 | struct AppleScriptHelper { 4 | 5 | static func runAppleScript(_ source: String) -> NSDictionary? { 6 | var error: NSDictionary? 7 | NSAppleScript(source: source)?.executeAndReturnError(&error) 8 | return error 9 | } 10 | 11 | static func launchScript(scriptName: String?, bundle: Bundle = Bundle.main) -> Bool? { 12 | 13 | guard let scriptFilePath = bundle.path(forResource: scriptName, ofType: "scpt") else { 14 | return nil 15 | } 16 | 17 | let contentOfFile = try? String(contentsOfFile: scriptFilePath) 18 | 19 | guard let appleScript = contentOfFile else { 20 | return nil 21 | } 22 | 23 | var error: NSDictionary? 24 | if let scriptObject = NSAppleScript(source: appleScript) { 25 | let output: NSAppleEventDescriptor = scriptObject.executeAndReturnError( 26 | &error) 27 | if error == nil { 28 | return output.booleanValue 29 | } else { 30 | print(error!) 31 | return nil 32 | } 33 | } 34 | 35 | return nil 36 | } 37 | 38 | static func getValueFrom(scriptName: String?, bundle: Bundle = Bundle.main) -> NSAppleEventDescriptor? { 39 | 40 | guard let scriptFilePath = bundle.path(forResource: scriptName, ofType: "scpt") else { 41 | return nil 42 | } 43 | 44 | let contentOfFile = try? String(contentsOfFile: scriptFilePath) 45 | 46 | guard let appleScript = contentOfFile else { 47 | return nil 48 | } 49 | 50 | var error: NSDictionary? 51 | if let scriptObject = NSAppleScript(source: appleScript) { 52 | let output: NSAppleEventDescriptor = scriptObject.executeAndReturnError( 53 | &error) 54 | if error == nil { 55 | return output 56 | } else { 57 | return nil 58 | } 59 | } 60 | 61 | return nil 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/BookmarkHelper.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objc 4 | class BookmarkHelper:NSObject { 5 | 6 | static func requestFullDiskAccess() { 7 | NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles")!) 8 | } 9 | 10 | static func hasFullDiskAccess() -> Bool { 11 | do { 12 | let homeUrl = try FileManager.default.url(for: .libraryDirectory, in: .userDomainMask, appropriateFor: nil, create: false) 13 | let file = homeUrl.appendingPathComponent("/Safari/Bookmarks.plist") 14 | 15 | if file.startAccessingSecurityScopedResource() { 16 | return NSDictionary(contentsOf: file) != nil 17 | } else { 18 | return false 19 | } 20 | } catch { 21 | return false 22 | } 23 | } 24 | 25 | static func getSafariBookmars() -> [[String: String]]? { 26 | do { 27 | let homeUrl = try FileManager.default.url(for: .libraryDirectory, in: .userDomainMask, appropriateFor: nil, create: false) 28 | let file = homeUrl.appendingPathComponent("/Safari/Bookmarks.plist") 29 | 30 | if file.startAccessingSecurityScopedResource() { 31 | if let plist = NSDictionary(contentsOf: file) { 32 | return extractBookmarksFrom(dict: plist) 33 | } else { 34 | return nil 35 | } 36 | } else { 37 | return nil 38 | } 39 | } catch { 40 | return nil 41 | } 42 | } 43 | 44 | static func extractBookmarksFrom(dict: NSDictionary) -> [[String: String]] { 45 | if let children: [NSDictionary] = dict["Children"] as! [NSDictionary]? { 46 | return (children.map(extractBookmarksFrom).flatMap{$0}.filter { !$0.isEmpty }) 47 | } else if let url: String = dict["URLString"] as! String? { 48 | let uriDictionary = (dict["URIDictionary"] as! NSDictionary?)! 49 | let title = (uriDictionary["title"] as! String?)! 50 | 51 | return [["title": title, "url": url]] 52 | } else { 53 | return [[:]] 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/CalendarHelper.h: -------------------------------------------------------------------------------- 1 | #ifndef CalendarHelper_h 2 | #define CalendarHelper_h 3 | 4 | #import 5 | 6 | @interface CalendarHelper : NSObject 7 | 8 | @property(nonatomic, strong) EKEventStore *store; 9 | 10 | - (void)requestCalendarAccess:(void (^)(void))callback; 11 | - (NSString *)getCalendarAuthorizationStatus; 12 | - (NSArray *)getEvents; 13 | 14 | @end 15 | #endif /* CalendarHelper_h */ 16 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/CalendarHelper.mm: -------------------------------------------------------------------------------- 1 | #import "CalendarHelper.h" 2 | 3 | @implementation CalendarHelper 4 | 5 | - (instancetype)init { 6 | self = [super init]; 7 | if (self) { 8 | _store = [[EKEventStore alloc] init]; 9 | } 10 | return self; 11 | } 12 | 13 | - (void)requestCalendarAccess:(void (^)(void))callback { 14 | if (@available(macOS 14.0, *)) { 15 | [_store requestFullAccessToEventsWithCompletion:^( 16 | BOOL granted, NSError *_Nullable error) { 17 | if (granted) { 18 | callback(); 19 | } else { 20 | NSLog(@"Access not granted %@", error); 21 | } 22 | }]; 23 | } else { 24 | [_store 25 | requestAccessToEntityType:EKEntityTypeEvent 26 | completion:^(BOOL granted, NSError *_Nullable error) { 27 | if (granted) { 28 | callback(); 29 | } else { 30 | NSLog(@"Access not granted %@", error); 31 | } 32 | }]; 33 | } 34 | } 35 | 36 | - (NSArray *)getEvents { 37 | NSArray *calendars = [_store calendarsForEntityType:EKEntityTypeEvent]; 38 | 39 | NSCalendar *gregorian = [[NSCalendar alloc] 40 | initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; 41 | NSDateComponents *fiveDaysComponent = [[NSDateComponents alloc] init]; 42 | fiveDaysComponent.day = 30; 43 | NSDate *nextDate = [gregorian dateByAddingComponents:fiveDaysComponent 44 | toDate:[NSDate date] 45 | options:0]; 46 | 47 | NSPredicate *predicate = [_store predicateForEventsWithStartDate:[NSDate date] 48 | endDate:nextDate 49 | calendars:calendars]; 50 | NSArray *events = [_store eventsMatchingPredicate:predicate]; 51 | return events; 52 | } 53 | 54 | - (NSString *)getCalendarAuthorizationStatus { 55 | EKAuthorizationStatus status = 56 | [EKEventStore authorizationStatusForEntityType:EKEntityTypeEvent]; 57 | switch (status) { 58 | case EKAuthorizationStatusDenied: 59 | return @"denied"; 60 | 61 | case EKAuthorizationStatusAuthorized: 62 | return @"authorized"; 63 | 64 | case EKAuthorizationStatusRestricted: 65 | return @"restricted"; 66 | 67 | case EKAuthorizationStatusNotDetermined: 68 | default: 69 | return @"notDetermined"; 70 | } 71 | } 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/ClipboardHelper.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class ClipboardHelper { 4 | static var frontmostApp: (name: String, bundle: String)? 5 | 6 | static func addOnCopyListener( 7 | _ callback: @escaping (_ pasteboard: NSPasteboard, _ app: (name: String, bundle: String)?) -> 8 | Void 9 | ) { 10 | let pasteboard = NSPasteboard.general 11 | var changeCount = NSPasteboard.general.changeCount 12 | 13 | NSWorkspace.shared.notificationCenter.addObserver( 14 | self, selector: #selector(frontmostAppChanged(sender:)), 15 | name: NSWorkspace.didActivateApplicationNotification, object: nil) 16 | 17 | // TODO find if there is any better way to observe for changes other than continously check if the amount of items has changed 18 | Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { _ in 19 | if pasteboard.changeCount != changeCount { 20 | callback(pasteboard, self.frontmostApp) 21 | changeCount = pasteboard.changeCount 22 | } 23 | } 24 | } 25 | 26 | @objc private static func frontmostAppChanged(sender: NSNotification) { 27 | if let info = sender.userInfo, 28 | let content = info[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication, 29 | let name = content.localizedName, 30 | let bundle = content.bundleIdentifier, 31 | let url = NSWorkspace.shared.urlForApplication(withBundleIdentifier: bundle) 32 | { 33 | frontmostApp = (name: name, bundle: url.absoluteString) 34 | } 35 | } 36 | 37 | static func insertToFrontmostApp(_ content: String) { 38 | DispatchQueue.main.async { 39 | PanelManager.shared.hideWindow() 40 | 41 | let source = CGEventSource(stateID: .hidSystemState) 42 | // event for key down event: 43 | let eventKey = CGEvent(keyboardEventSource: source, virtualKey: 0, keyDown: true) 44 | // event for key up event: 45 | let eventKeyUp = CGEvent(keyboardEventSource: source, virtualKey: 0, keyDown: false) 46 | 47 | var utf16array = Array(content.utf16) 48 | 49 | // set the emoji for the key down event: 50 | eventKey?.keyboardSetUnicodeString(stringLength: utf16array.count, unicodeString: &utf16array) 51 | // set the emoji for the key up event: 52 | eventKeyUp?.keyboardSetUnicodeString( 53 | stringLength: utf16array.count, unicodeString: &utf16array) 54 | // post key down event: 55 | eventKey?.post(tap: CGEventTapLocation.cghidEventTap) 56 | // post key up event: 57 | eventKeyUp?.post(tap: CGEventTapLocation.cghidEventTap) 58 | } 59 | } 60 | 61 | static func pasteToFrontmostApp(_ content: String) { 62 | DispatchQueue.main.async { 63 | PanelManager.shared.hideWindow() 64 | 65 | let pasteboard = NSPasteboard.general 66 | pasteboard.declareTypes([.string], owner: nil) 67 | 68 | pasteboard.setString(content, forType: .string) 69 | 70 | let event1 = CGEvent(keyboardEventSource: nil, virtualKey: 0x09, keyDown: true) // cmd-v down 71 | event1?.flags = CGEventFlags.maskCommand 72 | event1?.post(tap: CGEventTapLocation.cghidEventTap) 73 | 74 | let event2 = CGEvent(keyboardEventSource: nil, virtualKey: 0x09, keyDown: false) // cmd-v up 75 | // event2?.flags = CGEventFlags.maskCommand 76 | event2?.post(tap: CGEventTapLocation.cghidEventTap) 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/DarkMode.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct DarkMode { 4 | 5 | private static let prefix = "tell application \"System Events\" to tell appearance preferences to" 6 | 7 | static var isEnabled: Bool { 8 | get { 9 | return UserDefaults.standard.string(forKey: "AppleInterfaceStyle") == "Dark" 10 | } 11 | set { 12 | toggle(force: newValue) 13 | } 14 | } 15 | 16 | static func toggle(force: Bool? = nil) { 17 | let value = force.map(String.init) ?? "not dark mode" 18 | let _ = AppleScriptHelper.runAppleScript("\(prefix) set dark mode to \(value)") 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/DoNotDisturb.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Cocoa 3 | 4 | func getElement(target: AXUIElement, name: String) -> AXUIElement?{ 5 | 6 | var attributes: CFArray? = nil 7 | 8 | AXUIElementCopyAttributeNames(target, &attributes); 9 | 10 | guard let attributes = attributes else { 11 | return nil 12 | } 13 | 14 | for attribute in attributes as NSArray { 15 | print(attribute as! CFString) 16 | } 17 | 18 | assert((attributes as NSArray).contains(name)) 19 | 20 | 21 | var element: CFTypeRef? 22 | AXUIElementCopyAttributeValue(target, name as CFString, &element) 23 | 24 | guard let element = element else { 25 | return nil 26 | } 27 | 28 | return element as! AXUIElement? 29 | } 30 | 31 | func getChild(parent: AXUIElement, withTitle: String) -> AXUIElement? { 32 | 33 | var children: CFTypeRef? 34 | AXUIElementCopyAttributeValue(parent, "AXChildren" as CFString, &children) 35 | 36 | guard let children = children else { 37 | return nil 38 | } 39 | 40 | guard let items = children as? NSArray else { 41 | return nil 42 | } 43 | for x in items { 44 | let child = x as! AXUIElement 45 | 46 | var title: CFTypeRef? 47 | AXUIElementCopyAttributeValue(child, "AXTitle" as CFString, &title) 48 | 49 | guard let title = title else { 50 | continue 51 | } 52 | 53 | assert(CFGetTypeID(title) == CFStringGetTypeID()) 54 | 55 | if ((title as! CFString) as String) == withTitle { 56 | 57 | return child 58 | } 59 | } 60 | 61 | return nil 62 | } 63 | 64 | 65 | public struct DoNotDisturb { 66 | static func toggle() { 67 | 68 | let apps = NSWorkspace.shared.runningApplications 69 | 70 | guard let controlCenter = apps.first(where: {$0.bundleIdentifier == "com.apple.controlcenter"}) else { 71 | print("Control Center App not found!") 72 | return 73 | } 74 | 75 | let target = AXUIElementCreateApplication(controlCenter.processIdentifier) 76 | 77 | guard let menubar = getElement(target: target, name: "AXExtrasMenuBar") else { 78 | print("AXExtrasMenuBar not found!") 79 | return 80 | } 81 | 82 | guard let controlCenterMenu = getChild(parent: menubar, withTitle: "Control Center") else { 83 | print("\nControl Center not found!!") 84 | return 85 | } 86 | 87 | // Press the menu 88 | AXUIElementPerformAction(controlCenterMenu, kAXPressAction as CFString) 89 | 90 | // Now get the apps main window children since it will be showing 91 | guard let dialog = getElement(target: target, name: "AXFocusedWindow") else { 92 | print("AXFocusedWindow not found!") 93 | return 94 | } 95 | 96 | 97 | // Tries to find "focus" which means it is not enabled, then enables DND 98 | if let focusMenu = getChild(parent: dialog, withTitle: "Focus") { 99 | 100 | // Press the menu 101 | AXUIElementPerformAction(focusMenu, kAXPressAction as CFString) 102 | 103 | } else { 104 | // Tries to find do not disturb, then turns it off 105 | if let doNotDisturbMenu = getChild(parent: dialog, withTitle: "Do Not Disturb") { 106 | 107 | // Press the menu 108 | AXUIElementPerformAction(doNotDisturbMenu, kAXPressAction as CFString) 109 | 110 | } else { 111 | print("\nDo Not Disturb NOT found !!") 112 | } 113 | } 114 | 115 | // Press the menu 116 | AXUIElementPerformAction(controlCenterMenu, kAXPressAction as CFString) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/FS.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objc public class FS: NSObject { 4 | @objc static func ls(path: String) throws -> [String] { 5 | return try FileManager.default.contentsOfDirectory(atPath: path) 6 | } 7 | 8 | @objc static func exists(path: String) -> Bool { 9 | return FileManager.default.fileExists(atPath: path) 10 | } 11 | 12 | @objc static func readFile(path: String) -> String? { 13 | if FileManager.default.fileExists(atPath: path) { 14 | guard let data = FileManager.default.contents(atPath: path) else { 15 | return nil 16 | } 17 | 18 | let str = String(decoding: data, as: UTF8.self) 19 | return str 20 | } else { 21 | return nil 22 | } 23 | } 24 | 25 | static func copyFileFromUrl(_ url: URL, toPath path: String) throws { 26 | if exists(path: path) { 27 | try FileManager.default.removeItem(at: URL(fileURLWithPath: path)) 28 | } 29 | try FileManager.default.copyItem(at: url, to: URL(fileURLWithPath: path)) 30 | } 31 | 32 | static func delete(_ path: String) throws { 33 | try FileManager.default.removeItem(at: URL(fileURLWithPath: path)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/FileConstants.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct FileConstants { 4 | 5 | static let appHomeURL: URL = { 6 | let applicationSupportURL = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first 7 | let bundleIdentifier = Bundle.main.bundleIdentifier 8 | let appHomeURL = applicationSupportURL!.appendingPathComponent(bundleIdentifier!) 9 | try? FileManager.default.createDirectory(at: appHomeURL, withIntermediateDirectories: true, attributes: nil) 10 | return appHomeURL 11 | }() 12 | 13 | static let homeURL: URL = { 14 | let destinationURL = URL(fileURLWithPath: NSHomeDirectory()) 15 | return destinationURL 16 | }() 17 | 18 | static let tempURL: URL = { 19 | let destinationURL = URL(fileURLWithPath: NSTemporaryDirectory()) 20 | return destinationURL 21 | }() 22 | 23 | /// Queue used for reading and writing file promises. 24 | static let workQueue: OperationQueue = { 25 | let providerQueue = OperationQueue() 26 | providerQueue.qualityOfService = .userInitiated 27 | return providerQueue 28 | }() 29 | } 30 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/FileSearch.h: -------------------------------------------------------------------------------- 1 | #ifndef FileSearch_h 2 | #define FileSearch_h 3 | 4 | #include 5 | #include 6 | 7 | struct File { 8 | std::string path; 9 | bool is_folder; 10 | std::string name; 11 | }; 12 | 13 | std::vector search_files(NSString *basePath, NSString *query); 14 | 15 | #endif /* FileSearch_h */ 16 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/FileSearch.mm: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "FileSearch.h" 19 | #include "NSString+Score.h" 20 | 21 | std::vector search_files(NSString *basePath, NSString *query) { 22 | std::vector files; 23 | NSFileManager *defFM = [NSFileManager defaultManager]; 24 | NSError *error = nil; 25 | NSArray *dirPath = [defFM contentsOfDirectoryAtPath:basePath error:&error]; 26 | for(NSString *path in dirPath) { 27 | BOOL is_dir; 28 | NSString *full_path = [basePath stringByAppendingPathComponent:path]; 29 | std::string cpp_full_path = [full_path UTF8String]; 30 | float distance = [path scoreAgainst:query]; 31 | 32 | if([defFM fileExistsAtPath:full_path isDirectory:&is_dir] && is_dir){ 33 | if (distance > 0.5) { 34 | files.push_back({ 35 | .path = cpp_full_path, 36 | .is_folder = true, 37 | .name = [path UTF8String] 38 | }); 39 | } 40 | 41 | std::vector sub_files = search_files(full_path, query); 42 | files.insert(files.end(), sub_files.begin(), sub_files.end()); 43 | } else { 44 | 45 | if (distance > 0.5) { 46 | files.push_back({ 47 | .path = cpp_full_path, 48 | .is_folder = false, 49 | .name = [path UTF8String] 50 | }); 51 | } 52 | } 53 | } 54 | 55 | return files; 56 | } 57 | 58 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/JSIBindings.hpp: -------------------------------------------------------------------------------- 1 | #ifndef JSIBindings_hpp 2 | #define JSIBindings_hpp 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace sol { 9 | 10 | namespace jsi = facebook::jsi; 11 | namespace react = facebook::react; 12 | 13 | void install(jsi::Runtime &runtime, std::shared_ptr callInvoker); 14 | 15 | } 16 | 17 | #endif /* JSIBindings_hpp */ 18 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/JSIUtils.h: -------------------------------------------------------------------------------- 1 | #ifndef JSIUtils_h 2 | #define JSIUtils_h 3 | 4 | #import 5 | 6 | namespace sol { 7 | 8 | namespace jsi = facebook::jsi; 9 | 10 | NSString* jsiValueToNSString(jsi::Runtime &rt, const jsi::Value &v); 11 | double jsiValueToDouble(jsi::Runtime &rt, const jsi::Value &v); 12 | NSDate* jsiValueToNSDate(jsi::Runtime &rt, const jsi::Value &v); 13 | jsi::Value NSDateToJsiValue(jsi::Runtime &rt, NSDate* date); 14 | jsi::Value NSStringToJsiValue(jsi::Runtime &rt, NSString* v); 15 | 16 | } 17 | 18 | #endif /* JSIUtils_h */ 19 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/JSIUtils.mm: -------------------------------------------------------------------------------- 1 | #import 2 | #import "JSIUtils.h" 3 | 4 | namespace sol { 5 | 6 | namespace jsi = facebook::jsi; 7 | 8 | NSString* jsiValueToNSString(jsi::Runtime &rt, const jsi::Value &v) { 9 | if(!v.isString()) { 10 | throw jsi::JSError(rt, "Invalid string marshalling"); 11 | } 12 | 13 | auto str = v.asString(rt).utf8(rt); 14 | NSString* result = [[NSString alloc] initWithUTF8String:str.c_str()]; 15 | 16 | return result; 17 | } 18 | 19 | double jsiValueToDouble(jsi::Runtime &rt, const jsi::Value &v) { 20 | if(!v.isNumber()) { 21 | throw jsi::JSError(rt, "Invalid number marshalling"); 22 | } 23 | 24 | return v.asNumber(); 25 | } 26 | 27 | NSDate* jsiValueToNSDate(jsi::Runtime &rt, const jsi::Value &v) { 28 | if(!v.isNumber()) { 29 | throw jsi::JSError(rt, "Invalid date marshalling"); 30 | } 31 | 32 | NSDate *date = [NSDate dateWithTimeIntervalSince1970:static_cast(v.asNumber())]; 33 | 34 | return date; 35 | } 36 | 37 | jsi::Value NSDateToJsiValue(jsi::Runtime &rt, NSDate* date) { 38 | return jsi::Value(static_cast([date timeIntervalSince1970])); 39 | } 40 | 41 | jsi::Value NSStringToJsiValue(jsi::Runtime &rt, NSString* v) { 42 | auto res = jsi::String::createFromAscii(rt, [v UTF8String]); 43 | return res; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/MediaHelper.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import MediaPlayer 3 | 4 | struct MediaHelper { 5 | // This is an internal framework and it's bound to break itself as macOS updates... 6 | static func getCurrentMedia(callback: @escaping ([String: Any?]) -> Void) { 7 | // Load MediaRemote framework 8 | let bundle = CFBundleCreate( 9 | kCFAllocatorDefault, 10 | NSURL(fileURLWithPath: "/System/Library/PrivateFrameworks/MediaRemote.framework") 11 | ) 12 | 13 | // Get a Swift function for MRMediaRemoteGetNowPlayingInfo 14 | guard let MRMediaRemoteGetNowPlayingInfoPointer = 15 | CFBundleGetFunctionPointerForName( 16 | bundle, 17 | "MRMediaRemoteGetNowPlayingInfo" as CFString 18 | ) else { return } 19 | 20 | typealias MRMediaRemoteGetNowPlayingInfoFunction = 21 | @convention(c) (DispatchQueue, @escaping ([String: Any]) -> Void) -> Void 22 | let MRMediaRemoteGetNowPlayingInfo = 23 | unsafeBitCast(MRMediaRemoteGetNowPlayingInfoPointer, to: MRMediaRemoteGetNowPlayingInfoFunction.self) 24 | 25 | // Get a Swift function for MRNowPlayingClientGetBundleIdentifier 26 | guard let MRNowPlayingClientGetBundleIdentifierPointer = 27 | CFBundleGetFunctionPointerForName( 28 | bundle, 29 | "MRNowPlayingClientGetBundleIdentifier" as CFString) else { return } 30 | typealias MRNowPlayingClientGetBundleIdentifierFunction = @convention(c) (AnyObject?) -> String 31 | let MRNowPlayingClientGetBundleIdentifier = 32 | unsafeBitCast( 33 | MRNowPlayingClientGetBundleIdentifierPointer, 34 | to: MRNowPlayingClientGetBundleIdentifierFunction.self) 35 | 36 | // Get song info 37 | MRMediaRemoteGetNowPlayingInfo(DispatchQueue.main, { 38 | (information) in 39 | 40 | var bundleIdentifier: String? 41 | 42 | let _MRNowPlayingClientProtobuf: AnyClass? = NSClassFromString("MRClient") 43 | let handle : UnsafeMutableRawPointer! = dlopen("/usr/lib/libobjc.A.dylib", RTLD_NOW) 44 | let object = unsafeBitCast(dlsym(handle, "objc_msgSend"), to:(@convention(c)(AnyClass?,Selector?)->AnyObject).self)(_MRNowPlayingClientProtobuf,Selector("a"+"lloc")) 45 | unsafeBitCast(dlsym(handle, "objc_msgSend"), to:(@convention(c)(AnyObject?,Selector?,Any?)->Void).self)(object,Selector("i"+"nitWithData:"),information["kMRMediaRemoteNowPlayingInfoClientPropertiesData"] as AnyObject?) 46 | bundleIdentifier = MRNowPlayingClientGetBundleIdentifier(object) 47 | dlclose(handle) 48 | 49 | callback([ 50 | "kMRMediaRemoteNowPlayingInfoArtist": information["kMRMediaRemoteNowPlayingInfoArtist"], 51 | "kMRMediaRemoteNowPlayingInfoTitle": information["kMRMediaRemoteNowPlayingInfoTitle"], 52 | "kMRMediaRemoteNowPlayingInfoArtworkData": information["kMRMediaRemoteNowPlayingInfoArtworkData"], 53 | "bundleIdentifier": bundleIdentifier 54 | ]) 55 | 56 | }) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/MediaKeyForwarder.h: -------------------------------------------------------------------------------- 1 | #ifndef MediaKeyForwarder_h 2 | #define MediaKeyForwarder_h 3 | 4 | #import 5 | 6 | @interface MediaKeyForwarder:NSObject 7 | { 8 | CFMachPortRef eventPort; 9 | CFRunLoopSourceRef eventPortSource; 10 | } 11 | 12 | - (instancetype)init; 13 | - ( void ) startEventSession; 14 | - ( void ) stopEventSession; 15 | @end 16 | 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/MetadataExtractorError.swift: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // https://mczachurski.dev 4 | // Copyright © 2021 Marcin Czachurski and the repository contributors. 5 | // Licensed under the MIT License. 6 | // 7 | import Foundation 8 | 9 | public enum MetadataExtractorError: Error { 10 | case imageSourceNotCreated 11 | case imageMetadataNotCreated 12 | 13 | public var message: String { 14 | switch self { 15 | case .imageSourceNotCreated: 16 | return "CGImageSource object cannot be created." 17 | case .imageMetadataNotCreated: 18 | return "CGImageMetadata object cannot be created." 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/MouseUtils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | func getScreenWithMouse() -> NSScreen? { 4 | let mouseLocation = NSEvent.mouseLocation 5 | let screens = NSScreen.screens 6 | let screenWithMouse = (screens.first { NSMouseInRect(mouseLocation, $0.frame, false) }) 7 | return screenWithMouse 8 | } 9 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/NSString+Score.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Score.h 3 | // 4 | // Created by Nicholas Bruning on 5/12/11. 5 | // Copyright (c) 2011 Involved Pty Ltd. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | enum{ 11 | NSStringScoreOptionNone = 1 << 0, 12 | NSStringScoreOptionFavorSmallerWords = 1 << 1, 13 | NSStringScoreOptionReducedLongStringPenalty = 1 << 2 14 | }; 15 | 16 | typedef NSUInteger NSStringScoreOption; 17 | 18 | @interface NSString (Score) 19 | 20 | - (CGFloat) scoreAgainst:(NSString *)otherString; 21 | - (CGFloat) scoreAgainst:(NSString *)otherString fuzziness:(NSNumber *)fuzziness; 22 | - (CGFloat) scoreAgainst:(NSString *)otherString fuzziness:(NSNumber *)fuzziness options:(NSStringScoreOption)options; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/QRGenerator.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Cocoa 3 | 4 | func QR(from string: String, size len:CGFloat) -> NSImage? { 5 | let data = string.data(using: String.Encoding.ascii) 6 | if let filter = CIFilter(name: "CIQRCodeGenerator") { 7 | filter.setValue(data, forKey: "inputMessage") 8 | let trans = CGAffineTransform(scaleX: len, y: len) 9 | if let output = filter.outputImage?.transformed(by: trans) { 10 | let rep = NSCIImageRep(ciImage: output) 11 | let img = NSImage(size: rep.size) 12 | img.addRepresentation(rep) 13 | return img 14 | } 15 | } 16 | return nil 17 | } 18 | 19 | func WifiQR(name ssid: String, password code: String, size: CGFloat = 10) -> NSImage? { 20 | return QR(from: "WIFI:T:WPA;S:\(ssid);P:\(code);;", size: size) 21 | } 22 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/ShellHelper.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct ShellHelper { 4 | static func sh(_ command: String) -> String { 5 | let task = Process() 6 | let pipe = Pipe() 7 | 8 | task.standardOutput = pipe 9 | task.standardError = pipe 10 | task.arguments = ["-c", command] 11 | task.launchPath = "/bin/sh" 12 | task.standardInput = nil 13 | task.launch() 14 | 15 | let data = pipe.fileHandleForReading.readDataToEndOfFile() 16 | let output = String(data: data, encoding: .utf8)! 17 | 18 | return output 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/SolEmitter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class SolEmitter { 4 | 5 | var hasListeners = false 6 | 7 | public static var sharedInstance = SolEmitter() 8 | 9 | private static var emitter: SolNative! 10 | 11 | func registerEmitter(emitter: SolNative) { 12 | SolEmitter.emitter = emitter 13 | } 14 | 15 | func dispatch(name: String, body: Any?) { 16 | if hasListeners { 17 | SolEmitter.emitter.sendEvent(withName: name, body: body) 18 | } 19 | } 20 | 21 | // You can add more typesafety here if you want to 22 | func keyDown(key: String?, keyCode: UInt16, meta: Bool, shift: Bool, control: Bool) { 23 | dispatch( 24 | name: "keyDown", 25 | body: [ 26 | "key": key!, 27 | "keyCode": keyCode, 28 | "meta": meta, 29 | "shift": shift, 30 | "control": control, 31 | ]) 32 | } 33 | 34 | func keyUp(key: String?, keyCode: UInt16, meta: Bool, shift: Bool, control: Bool) { 35 | dispatch( 36 | name: "keyUp", 37 | body: [ 38 | "key": key!, 39 | "keyCode": keyCode, 40 | "meta": meta, 41 | "shift": shift, 42 | "control": control, 43 | ]) 44 | } 45 | 46 | func onShow(target: String?) { 47 | dispatch( 48 | name: "onShow", 49 | body: [ 50 | "target": target 51 | ]) 52 | } 53 | 54 | func onHotkey(id: String) { 55 | dispatch( 56 | name: "hotkey", 57 | body: [ 58 | "id": id 59 | ]) 60 | } 61 | 62 | func onHide() { 63 | dispatch(name: "onHide", body: []) 64 | } 65 | 66 | func textCopied(_ txt: String, _ bundle: String?) { 67 | dispatch( 68 | name: "onTextCopied", 69 | body: [ 70 | "text": txt, 71 | "bundle": bundle, 72 | ]) 73 | } 74 | 75 | func fileCopied(_ text: String, _ url: String, _ bundle: String?) { 76 | dispatch( 77 | name: "onFileCopied", 78 | body: [ 79 | "text": text, 80 | "url": url, 81 | "bundle": bundle, 82 | ]) 83 | } 84 | 85 | func onStatusBarItemClick() { 86 | dispatch(name: "onStatusBarItemClick", body: []) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/SolMacros.h: -------------------------------------------------------------------------------- 1 | // 2 | // SolMacros.h 3 | // sol 4 | // 5 | // Created by Oscar on 28.08.22. 6 | // 7 | 8 | #ifndef SolMacros_h 9 | #define SolMacros_h 10 | 11 | #define HOSTFN(name, size, capture) \ 12 | jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, name), size, \ 13 | capture(jsi::Runtime &rt, const jsi::Value &thisValue, \ 14 | const jsi::Value *arguments, size_t count) \ 15 | ->jsi::Value 16 | 17 | 18 | #define JSIFN(capture) \ 19 | capture(jsi::Runtime &rt, const jsi::Value &thisValue, \ 20 | const jsi::Value *arguments, size_t count) \ 21 | ->jsi::Value 22 | 23 | #endif /* SolMacros_h */ 24 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/SpotifyApplication.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Spotify.h 3 | */ 4 | 5 | #import 6 | #import 7 | 8 | 9 | @class SpotifyApplication, SpotifyTrack, SpotifyApplication; 10 | 11 | enum SpotifyEPlS { 12 | SpotifyEPlSStopped = 'kPSS', 13 | SpotifyEPlSPlaying = 'kPSP', 14 | SpotifyEPlSPaused = 'kPSp' 15 | }; 16 | typedef enum SpotifyEPlS SpotifyEPlS; 17 | 18 | 19 | 20 | /* 21 | * Spotify Suite 22 | */ 23 | 24 | // The Spotify application. 25 | @interface SpotifyApplication : SBApplication 26 | 27 | @property (copy, readonly) SpotifyTrack *currentTrack; // The current playing track. 28 | @property NSInteger soundVolume; // The sound output volume (0 = minimum, 100 = maximum) 29 | @property (readonly) SpotifyEPlS playerState; // Is Spotify stopped, paused, or playing? 30 | @property double playerPosition; // The player’s position within the currently playing track in seconds. 31 | @property (readonly) BOOL repeatingEnabled; // Is repeating enabled in the current playback context? 32 | @property BOOL repeating; // Is repeating on or off? 33 | @property (readonly) BOOL shufflingEnabled; // Is shuffling enabled in the current playback context? 34 | @property BOOL shuffling; // Is shuffling on or off? 35 | 36 | - (void) nextTrack; // Skip to the next track. 37 | - (void) previousTrack; // Skip to the previous track. 38 | - (void) playpause; // Toggle play/pause. 39 | - (void) pause; // Pause playback. 40 | - (void) play; // Resume playback. 41 | - (void) playTrack:(NSString *)x inContext:(NSString *)inContext; // Start playback of a track in the given context. 42 | 43 | @end 44 | 45 | // A Spotify track. 46 | @interface SpotifyTrack : SBObject 47 | 48 | @property (copy, readonly) NSString *artist; // The artist of the track. 49 | @property (copy, readonly) NSString *album; // The album of the track. 50 | @property (readonly) NSInteger discNumber; // The disc number of the track. 51 | @property (readonly) NSInteger duration; // The length of the track in seconds. 52 | @property (readonly) NSInteger playedCount; // The number of times this track has been played. 53 | @property (readonly) NSInteger trackNumber; // The index of the track in its album. 54 | @property (readonly) BOOL starred; // Is the track starred? 55 | @property (readonly) NSInteger popularity; // How popular is this track? 0-100 56 | - (NSString *) id; // The ID of the item. 57 | @property (copy, readonly) NSString *name; // The name of the track. 58 | @property (copy, readonly) NSString *artworkUrl; // The URL of the track%apos;s album cover. 59 | @property (copy, readonly) NSImage *artwork; // The property is deprecated and will never be set. Use the 'artwork url' instead. 60 | @property (copy, readonly) NSString *albumArtist; // That album artist of the track. 61 | @property (copy) NSString *spotifyUrl; // The URL of the track. 62 | 63 | 64 | @end 65 | 66 | 67 | 68 | /* 69 | * Standard Suite 70 | */ 71 | 72 | // The application's top level scripting object. 73 | @interface SpotifyApplication (StandardSuite) 74 | 75 | @property (copy, readonly) NSString *name; // The name of the application. 76 | @property (readonly) BOOL frontmost; // Is this the frontmost (active) application? 77 | @property (copy, readonly) NSString *version; // The version of the application. 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/processes.h: -------------------------------------------------------------------------------- 1 | //#import 2 | // 3 | //NSArray* allProcesses(); 4 | //BOOL processIsRunning(NSString* executableName, NSArray* processes); 5 | -------------------------------------------------------------------------------- /macos/sol-macOS/lib/processes.mm: -------------------------------------------------------------------------------- 1 | //#include "processes.h" 2 | //#import 3 | // 4 | //NSArray* allProcesses(){ 5 | // static int maxArgumentSize = 0; 6 | // if (maxArgumentSize == 0) { 7 | // size_t size = sizeof(maxArgumentSize); 8 | // if (sysctl((int[]){ CTL_KERN, KERN_ARGMAX }, 2, &maxArgumentSize, &size, NULL, 0) == -1) { 9 | // perror("sysctl argument size"); 10 | // maxArgumentSize = 4096; // Default 11 | // } 12 | // } 13 | // NSMutableArray *processes = [NSMutableArray array]; 14 | // int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL}; 15 | // struct kinfo_proc *info; 16 | // size_t length; 17 | // int count; 18 | // 19 | // if (sysctl(mib, 3, NULL, &length, NULL, 0) < 0) 20 | // return nil; 21 | // if (!(info = malloc(length))) 22 | // return nil; 23 | // if (sysctl(mib, 3, info, &length, NULL, 0) < 0) { 24 | // free(info); 25 | // return nil; 26 | // } 27 | // count = length / sizeof(struct kinfo_proc); 28 | // for (int i = 0; i < count; i++) { 29 | // pid_t pid = info[i].kp_proc.p_pid; 30 | // if (pid == 0) { 31 | // continue; 32 | // } 33 | // size_t size = maxArgumentSize; 34 | // char* buffer = (char *)malloc(length); 35 | // if (sysctl((int[]){ CTL_KERN, KERN_PROCARGS2, pid }, 3, buffer, &size, NULL, 0) == 0) { 36 | // NSString* executable = [NSString stringWithCString:(buffer+sizeof(int)) encoding:NSUTF8StringEncoding]; 37 | // [processes addObject:[NSDictionary dictionaryWithObjectsAndKeys: 38 | // [NSNumber numberWithInt:pid], @"pid", 39 | // executable, @"executable", 40 | // nil]]; 41 | // } 42 | // free(buffer); 43 | // } 44 | // 45 | // free(info); 46 | // 47 | // return processes; 48 | //} 49 | // 50 | //BOOL processIsRunning(NSString* executableName, NSArray* processes){ 51 | // if (!processes) { 52 | // processes = allProcesses(); 53 | // } 54 | // BOOL searchIsPath = [executableName isAbsolutePath]; 55 | // NSEnumerator* processEnumerator = [processes objectEnumerator]; 56 | // NSDictionary* process; 57 | // while ((process = (NSDictionary*)[processEnumerator nextObject])) { 58 | // NSString* executable = [process objectForKey:@"executable"]; 59 | // if ([(searchIsPath ? executable : [executable lastPathComponent]) isEqual:executableName]) { 60 | // return YES; 61 | // } 62 | // } 63 | // return NO; 64 | //} 65 | -------------------------------------------------------------------------------- /macos/sol-macOS/managers/StatusBarItemManager.swift: -------------------------------------------------------------------------------- 1 | class StatusBarItemManager { 2 | static public let shared = StatusBarItemManager() 3 | 4 | private var statusBarItem: NSStatusItem? 5 | 6 | @objc func statusBarItemCallback(_: AnyObject?) { 7 | SolEmitter.sharedInstance.onStatusBarItemClick() 8 | } 9 | 10 | func setStatusBarTitle(_ title: String) { 11 | DispatchQueue.main.async { 12 | if self.statusBarItem == nil { 13 | self.statusBarItem = NSStatusBar.system 14 | .statusItem(withLength: NSStatusItem.variableLength) 15 | } 16 | 17 | if title.isEmpty { 18 | NSStatusBar.system.removeStatusItem(self.statusBarItem!) 19 | self.statusBarItem = nil 20 | } else { 21 | if let button = self.statusBarItem?.button { 22 | button.title = title 23 | button.action = #selector(self.statusBarItemCallback(_:)) 24 | button.target = self 25 | button.sizeToFit() 26 | } 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /macos/sol-macOS/managers/ToastManager.swift: -------------------------------------------------------------------------------- 1 | class ToastManager { 2 | private var toastWindow: Toast = Toast(contentRect: .zero) 3 | 4 | static public let shared = ToastManager() 5 | 6 | func showToast( 7 | _ text: String, variant: String, timeout: NSNumber?, image: NSImage? 8 | ) { 9 | guard let screen = PanelManager.shared.getPreferredScreen() 10 | else { 11 | return 12 | } 13 | 14 | let variantEnum: ToastVariant = 15 | switch variant { 16 | case "error": .error 17 | case "success": .success 18 | default: .none 19 | } 20 | 21 | let toastView = ToastView( 22 | text: text, 23 | variant: variantEnum, 24 | image: image, 25 | dismissCallback: { 26 | DispatchQueue.main.async { 27 | self.dismissToast() 28 | } 29 | } 30 | ) 31 | toastView.layoutSubtreeIfNeeded() 32 | 33 | let contentSize = toastView.intrinsicContentSize 34 | let size = NSRect( 35 | x: 0, y: 0, width: contentSize.width, height: contentSize.height) 36 | toastView.frame = size 37 | 38 | let effectView = NSVisualEffectView( 39 | frame: size 40 | ) 41 | effectView.autoresizingMask = [.width, .height] 42 | effectView.material = .hudWindow // Or other material 43 | effectView.blendingMode = .behindWindow 44 | effectView.state = .active 45 | 46 | toastWindow.contentView = effectView 47 | toastWindow.contentView!.addSubview(toastView) 48 | 49 | // Add the effect view to the content view of the window, NOT the ToastView 50 | toastWindow 51 | .setFrame( 52 | size, 53 | display: true 54 | ) 55 | toastWindow.setContentSize(contentSize) 56 | 57 | // 0 is calculated from the origin of the main screen, which means screen.frame.origin.x can be negative 58 | let x = 59 | screen.frame.origin.x 60 | + (screen.frame.size.width / 2 - contentSize.width / 2) 61 | var y = screen.frame.origin.y + screen.frame.size.height * 0.1 62 | 63 | if image != nil { 64 | y = screen.frame.origin.y + toastWindow.frame.height 65 | } 66 | 67 | toastWindow.setFrameOrigin( 68 | NSPoint( 69 | x: x, 70 | y: y 71 | )) 72 | 73 | toastWindow.makeKeyAndOrderFront(nil) 74 | 75 | let deadline = 76 | timeout != nil ? DispatchTime.now() + timeout!.doubleValue : .now() + 2 77 | DispatchQueue.main.asyncAfter(deadline: deadline) { 78 | self.dismissToast() 79 | } 80 | } 81 | 82 | func dismissToast() { 83 | toastWindow.orderOut(nil) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /macos/sol-macOS/sol-macOS-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "MediaKeyForwarder.h" 2 | #import 3 | #import 4 | #import 5 | #import 6 | #import 7 | #import 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | 14 | AXError _AXUIElementGetWindow(AXUIElementRef element, uint32_t *identifier); 15 | -------------------------------------------------------------------------------- /macos/sol-macOS/sol-macOS.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.automation.apple-events 6 | 7 | com.apple.security.network.client 8 | 9 | com.apple.security.files.user-selected.read-only 10 | 11 | com.apple.security.personal-information.calendars 12 | 13 | com.apple.security.personal-information.location 14 | 15 | com.apple.security.temporary-exception.apple-events 16 | 17 | com.apple.systemevents 18 | com.apple.systempreferences 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /macos/sol-macOS/sol.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.automation.apple-events 6 | 7 | com.apple.security.app-sandbox 8 | 9 | com.apple.security.files.user-selected.read-only 10 | 11 | com.apple.security.network.client 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /macos/sol-macOS/views/BlurView.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface RCT_EXTERN_MODULE(BlurViewManager, RCTViewManager) 4 | RCT_EXPORT_VIEW_PROPERTY(startColor,NSString) 5 | RCT_EXPORT_VIEW_PROPERTY(endColor,NSString) 6 | RCT_EXPORT_VIEW_PROPERTY(cornerRadius,double) 7 | RCT_EXPORT_VIEW_PROPERTY(disabled, BOOL) 8 | RCT_EXPORT_VIEW_PROPERTY(materialName, NSString) 9 | @end 10 | 11 | -------------------------------------------------------------------------------- /macos/sol-macOS/views/BlurView.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | 3 | class BlurView: NSVisualEffectView { 4 | @objc var borderRadius: Double = 0 5 | @objc var inViewBlur: Bool = false 6 | @objc var disabled: Bool = false 7 | @objc var materialName: String = "windowBackground" 8 | 9 | override var isFlipped: Bool { 10 | return true 11 | } 12 | 13 | override func didSetProps(_: [String]!) { 14 | if disabled { 15 | material = .contentBackground 16 | return 17 | } 18 | 19 | layer?.cornerRadius = borderRadius 20 | if materialName == "windowBackground" { 21 | material = .windowBackground 22 | } else if materialName == "menu" { 23 | material = .menu 24 | } else if materialName == "sidebar" { 25 | material = .sidebar 26 | } else if materialName == "header" { 27 | material = .headerView 28 | } else if materialName == "sheet" { 29 | material = .sheet 30 | } else if materialName == "popover" { 31 | material = .popover 32 | } else if materialName == "hudWindow" { 33 | material = .hudWindow 34 | } else if materialName == "fullScreenUI" { 35 | material = .fullScreenUI 36 | } 37 | } 38 | 39 | override func convert(_ point: NSPoint, from _: NSView?) -> NSPoint { 40 | return NSPoint(x: point.x, y: frame.height - point.y) 41 | } 42 | 43 | override init(frame: CGRect) { 44 | super.init(frame: frame) 45 | 46 | if disabled { 47 | material = .contentBackground 48 | return 49 | } 50 | 51 | if materialName == "windowBackground" { 52 | material = .windowBackground 53 | } else if materialName == "menu" { 54 | material = .menu 55 | } else if materialName == "sidebar" { 56 | material = .sidebar 57 | } else if materialName == "header" { 58 | material = .headerView 59 | } else if materialName == "sheet" { 60 | material = .sheet 61 | } else if materialName == "popover" { 62 | material = .popover 63 | } else if materialName == "hudWindow" { 64 | material = .hudWindow 65 | } else if materialName == "fullScreenUI" { 66 | material = .fullScreenUI 67 | } 68 | 69 | wantsLayer = true 70 | layer?.cornerRadius = borderRadius 71 | layer?.masksToBounds = true 72 | } 73 | 74 | required init?(coder aDecoder: NSCoder) { 75 | super.init(coder: aDecoder) 76 | } 77 | } 78 | 79 | @objc(BlurViewManager) 80 | class BlurViewManager: RCTViewManager { 81 | override static func requiresMainQueueSetup() -> Bool { 82 | return true 83 | } 84 | 85 | override func view() -> NSView! { 86 | return BlurView() 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /macos/sol-macOS/views/FileIcon.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface RCT_EXTERN_MODULE(FileIconManager, RCTViewManager) 4 | RCT_EXPORT_VIEW_PROPERTY(url,NSString) 5 | @end 6 | 7 | -------------------------------------------------------------------------------- /macos/sol-macOS/views/FileIcon.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | 3 | class FileIcon: NSView { 4 | let image = NSImageView() 5 | @objc var url: NSString = "" { 6 | didSet { 7 | self.setupView() 8 | } 9 | } 10 | 11 | override init(frame: CGRect) { 12 | super.init(frame: frame) 13 | } 14 | 15 | required init?(coder aDecoder: NSCoder) { 16 | super.init(coder: aDecoder) 17 | } 18 | 19 | private func setupView() { 20 | let url = URL(fileURLWithPath: (self.url as String).replacingOccurrences(of: "~", with: FileManager.default.homeDirectoryForCurrentUser.path)) 21 | 22 | let icon = NSWorkspace.shared.icon(forFile: url.path) 23 | self.image.image = icon 24 | image.autoresizingMask = [.height, .width] 25 | self.addSubview(image) 26 | } 27 | } 28 | 29 | @objc(FileIconManager) 30 | class FileIconManager: RCTViewManager { 31 | 32 | override static func requiresMainQueueSetup() -> Bool { 33 | return true 34 | } 35 | 36 | override func view() -> NSView! { 37 | return FileIcon() 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /macos/sol-macOS/views/GradientView.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface RCT_EXTERN_MODULE(GradientViewManager, RCTViewManager) 4 | RCT_EXPORT_VIEW_PROPERTY(startColor,NSString) 5 | RCT_EXPORT_VIEW_PROPERTY(endColor,NSString) 6 | RCT_EXPORT_VIEW_PROPERTY(angle,double) 7 | RCT_EXPORT_VIEW_PROPERTY(cornerRadius,double) 8 | @end 9 | 10 | -------------------------------------------------------------------------------- /macos/sol-macOS/views/GradientView.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | 3 | class GradientView: NSView { 4 | 5 | @objc var startColor: NSString = "" 6 | @objc var endColor: NSString = "" 7 | @objc var angle: Double = 0 8 | @objc var cornerRadius: Double = 0 9 | 10 | var bgGradient: CAGradientLayer! 11 | 12 | override var isFlipped: Bool { 13 | return true 14 | } 15 | 16 | override func didSetProps(_ changedProps: [String]!) { 17 | let gradient2 = CAGradientLayer() 18 | gradient2.frame = CGRect(origin: CGPoint.zero, size: self.frame.size) 19 | gradient2.colors = [NSColor(hex: self.startColor)!.cgColor, NSColor(hex: self.endColor)!.cgColor] 20 | gradient2.apply(angle: self.angle) 21 | 22 | self.layer?.replaceSublayer(bgGradient, with: gradient2) 23 | bgGradient = gradient2 24 | 25 | self.layer?.cornerRadius = self.cornerRadius 26 | } 27 | 28 | // Required to flip touch events 29 | override func convert(_ point: NSPoint, from _: NSView?) -> NSPoint { 30 | return NSPoint(x: point.x, y: frame.height - point.y) 31 | } 32 | 33 | override var frame: CGRect { 34 | didSet { 35 | let gradient2 = CAGradientLayer() 36 | gradient2.frame = CGRect(origin: CGPoint.zero, size: self.frame.size) 37 | gradient2.colors = [NSColor(hex: self.startColor)?.cgColor, NSColor(hex: self.endColor)?.cgColor] 38 | gradient2.apply(angle: self.angle) 39 | 40 | self.layer?.replaceSublayer(bgGradient, with: gradient2) 41 | bgGradient = gradient2 42 | } 43 | } 44 | 45 | 46 | override init(frame: CGRect) { 47 | super.init(frame: frame) 48 | self.wantsLayer = true 49 | 50 | let gradient2 = CAGradientLayer() 51 | gradient2.frame = CGRect(origin: CGPoint.zero, size: self.frame.size) 52 | gradient2.colors = [] 53 | gradient2.apply(angle: 135) 54 | 55 | self.layer?.addSublayer(gradient2) 56 | bgGradient = gradient2 57 | } 58 | 59 | 60 | 61 | required init?(coder aDecoder: NSCoder) { 62 | super.init(coder: aDecoder) 63 | } 64 | } 65 | 66 | @objc(GradientViewManager) 67 | class GradientViewManager: RCTViewManager { 68 | 69 | override static func requiresMainQueueSetup() -> Bool { 70 | return true 71 | } 72 | 73 | override func view() -> NSView! { 74 | return GradientView() 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /macos/sol-macOS/views/Overlay.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | final class Overlay: NSWindow, NSWindowDelegate { 4 | init(contentRect: NSRect, backing: NSWindow.BackingStoreType, defer flag: Bool) { 5 | super.init( 6 | contentRect: contentRect, 7 | styleMask: [.borderless], 8 | backing: backing, 9 | defer: flag 10 | ) 11 | 12 | self.level = .mainMenu + 2 13 | self.backgroundColor = .black 14 | self.ignoresMouseEvents = true 15 | } 16 | 17 | override func constrainFrameRect(_ frameRect: NSRect, to screen: NSScreen?) -> NSRect { 18 | return frameRect 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /macos/sol-macOS/views/Panel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | let appDelegate = NSApp.delegate as? AppDelegate 4 | 5 | final class Panel: NSPanel, NSWindowDelegate { 6 | init(contentRect: NSRect) { 7 | super.init( 8 | contentRect: contentRect, 9 | styleMask: [.titled, .fullSizeContentView, .nonactivatingPanel], 10 | backing: .buffered, 11 | defer: false 12 | ) 13 | 14 | self.hasShadow = true 15 | self.level = .floating 16 | self.collectionBehavior.insert(.fullScreenAuxiliary) 17 | self.collectionBehavior.insert(.canJoinAllSpaces) 18 | self.titleVisibility = .hidden 19 | self.titlebarAppearsTransparent = true 20 | self.isMovableByWindowBackground = true 21 | self.isReleasedWhenClosed = false 22 | self.isOpaque = false 23 | self.delegate = self 24 | self.backgroundColor = NSColor.windowBackgroundColor.withAlphaComponent(0.2) 25 | 26 | let effectView = NSVisualEffectView( 27 | frame: .zero 28 | ) 29 | effectView.autoresizingMask = [.width, .height] 30 | effectView.material = .headerView 31 | effectView.blendingMode = .behindWindow 32 | effectView.state = .active 33 | 34 | self.contentView = effectView 35 | self.contentView!.wantsLayer = true 36 | } 37 | 38 | override var canBecomeKey: Bool { 39 | return true 40 | } 41 | 42 | override var canBecomeMain: Bool { 43 | return true 44 | } 45 | 46 | func windowDidResignKey(_ notification: Notification) { 47 | DispatchQueue.main.async { 48 | PanelManager.shared.hideWindow() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /macos/sol-macOS/views/Toast.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | final class Toast: NSPanel, NSWindowDelegate { 4 | init(contentRect: NSRect) { 5 | super.init( 6 | contentRect: contentRect, 7 | styleMask: [.titled, .fullSizeContentView, .nonactivatingPanel], 8 | backing: .buffered, 9 | defer: false 10 | ) 11 | 12 | self.level = .floating 13 | self.collectionBehavior.insert(.fullScreenAuxiliary) 14 | self.collectionBehavior.insert(.canJoinAllSpaces) 15 | self.titleVisibility = .hidden 16 | self.titlebarAppearsTransparent = true 17 | self.isMovable = false 18 | self.isMovableByWindowBackground = false 19 | self.isReleasedWhenClosed = false 20 | self.isOpaque = false 21 | self.setFrameAutosaveName("") 22 | } 23 | 24 | override var canBecomeKey: Bool { 25 | return false 26 | } 27 | 28 | override var canBecomeMain: Bool { 29 | return false 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /macos/sol.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "originHash" : "78526aafdff3d7bb5fccbf1f4499af4d3ea88e16e8819bf30db26c60f51192d8", 3 | "pins" : [ 4 | { 5 | "identity" : "launchatlogin-legacy", 6 | "kind" : "remoteSourceControl", 7 | "location" : "https://github.com/sindresorhus/LaunchAtLogin-Legacy", 8 | "state" : { 9 | "branch" : "main", 10 | "revision" : "9a894d799269cb591037f9f9cb0961510d4dca81" 11 | } 12 | } 13 | ], 14 | "version" : 3 15 | } 16 | -------------------------------------------------------------------------------- /macos/sol.xcodeproj/xcshareddata/xcschemes/debug.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 58 | 59 | 60 | 61 | 67 | 69 | 75 | 76 | 77 | 78 | 80 | 81 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /macos/sol.xcodeproj/xcshareddata/xcschemes/release.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 58 | 59 | 60 | 61 | 67 | 69 | 75 | 76 | 77 | 78 | 80 | 81 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /macos/sol.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /macos/sol.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/sol.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /macos/sol.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "originHash" : "78526aafdff3d7bb5fccbf1f4499af4d3ea88e16e8819bf30db26c60f51192d8", 3 | "pins" : [ 4 | { 5 | "identity" : "launchatlogin-legacy", 6 | "kind" : "remoteSourceControl", 7 | "location" : "https://github.com/sindresorhus/LaunchAtLogin-Legacy", 8 | "state" : { 9 | "branch" : "main", 10 | "revision" : "9a894d799269cb591037f9f9cb0961510d4dca81" 11 | } 12 | } 13 | ], 14 | "version" : 3 15 | } 16 | -------------------------------------------------------------------------------- /metro.config.js: -------------------------------------------------------------------------------- 1 | const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config') 2 | const { withNativeWind } = require('nativewind/metro') 3 | 4 | const config = mergeConfig(getDefaultConfig(__dirname), {}) 5 | 6 | module.exports = withNativeWind(config, {input: './src/global.css'}) 7 | -------------------------------------------------------------------------------- /mise.toml: -------------------------------------------------------------------------------- 1 | [tools] 2 | bun = "latest" 3 | ruby = "3.3.0" 4 | 5 | [hooks] 6 | postinstall = "bun install && gem install bundler && bundle install && bun pods" 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sol", 3 | "version": "2.1.200", 4 | "private": true, 5 | "scripts": { 6 | "start": "react-native start", 7 | "macos": "react-native run-macos --scheme debug", 8 | "bump": "./scripts/bump-version.sh", 9 | "permissions:reset": "tccutil reset Calendar com.ospfranco.sol && tccutil reset Accessibility com.ospfranco.sol", 10 | "dev": "bundle exec fastlane dev && rimraf releases/Sol.app && open /Applications/Sol.app", 11 | "release": "bundle exec fastlane release", 12 | "fix-dependencies": "rnx-align-deps --write", 13 | "appcast": "./macos/Pods/Sparkle/bin/generate_appcast releases", 14 | "pods": "cd macos && bundle exec pod install && rm .xcode.env.local && xcodebuild -resolvePackageDependencies -workspace sol.xcworkspace -scheme debug", 15 | "typecheck": "tsc --noEmit" 16 | }, 17 | "dependencies": { 18 | "@expo/plist": "^0.0.20", 19 | "@react-native-async-storage/async-storage": "^1.23.1", 20 | "@sentry/react-native": "5.35.0", 21 | "assert": "^2.0.0", 22 | "axios": "^0.25.0", 23 | "chance": "^1.1.9", 24 | "clsx": "^1.2.1", 25 | "expr-eval": "^2.0.2", 26 | "fuse.js": "7.0.0", 27 | "intl": "^1.2.5", 28 | "lodash": "^4.17.21", 29 | "luxon": "^2.3.0", 30 | "minisearch": "7.1.1", 31 | "mobx": "^6.13.5", 32 | "mobx-react-lite": "^4.1.0", 33 | "nanoid": "^4.0.0", 34 | "nativewind": "4.0.36", 35 | "pretty-bytes": "^6.1.1", 36 | "react": "19.0.0", 37 | "react-native": "^0.78.2", 38 | "react-native-get-random-values": "^1.8.0", 39 | "react-native-mmkv": "2.12.2", 40 | "react-native-macos": "^0.78.3", 41 | "react-native-reanimated": "^3.8.1", 42 | "uuid": "^9.0.0" 43 | }, 44 | "devDependencies": { 45 | "@babel/core": "^7.25.2", 46 | "@babel/preset-env": "^7.25.3", 47 | "@babel/runtime": "^7.25.0", 48 | "@react-native-community/cli": "15.0.1", 49 | "@react-native-community/cli-platform-android": "15.0.1", 50 | "@react-native-community/cli-platform-ios": "15.0.1", 51 | "@react-native/babel-preset": "0.78.2", 52 | "@react-native/eslint-config": "0.78.2", 53 | "@react-native/metro-config": "0.78.2", 54 | "@react-native/typescript-config": "0.78.2", 55 | "@types/chance": "^1.1.3", 56 | "@types/lodash": "^4.14.182", 57 | "@types/luxon": "^2.0.9", 58 | "@types/react": "^19.0.0", 59 | "@types/react-test-renderer": "^19.0.0", 60 | "@types/uuid": "^9.0.0", 61 | "babel-jest": "^29.6.3", 62 | "babel-plugin-module-resolver": "^4.1.0", 63 | "eslint": "^8.19.0", 64 | "prettier": "^2.8.8", 65 | "rimraf": "^3.0.2", 66 | "tailwindcss": "3.4.3", 67 | "typescript": "5.0.4" 68 | }, 69 | "packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610" 70 | } 71 | -------------------------------------------------------------------------------- /releases/appcast.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sol 5 | 6 | 2.1.200 7 | Sun, 01 Jun 2025 09:18:42 +0200 8 | 414 9 | 2.1.200 10 | 11.0 11 | 12 | 13 | 14 | 2.1.199 15 | Sat, 31 May 2025 20:34:02 +0200 16 | 413 17 | 2.1.199 18 | 11.0 19 | 20 | 21 | 22 | 2.1.198 23 | Thu, 29 May 2025 13:21:43 +0200 24 | 412 25 | 2.1.198 26 | 11.0 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /releases/old_updates/Sol406-405.delta: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/releases/old_updates/Sol406-405.delta -------------------------------------------------------------------------------- /scripts/appcast.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | url="$1" 4 | version="$2" 5 | 6 | url_to_replace="https://raw.githubusercontent.com/ospfranco/sol/main/releases/$version.zip" 7 | 8 | # XML template 9 | # Read XML content from file 10 | xml=$(cat releases/appcast.xml) 11 | 12 | # Replace the url 13 | xml=${xml//"$url_to_replace"/"$url"} 14 | 15 | # Save XML content to a file 16 | echo "$xml" > releases/appcast.xml 17 | 18 | echo "XML file generated successfully." -------------------------------------------------------------------------------- /scripts/bump-version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | npm --no-git-tag-version version patch 6 | 7 | # DON'T BE SILLY REPLACE THIS FOR YOUR DIRECTORY 8 | PROJECT_DIR="macos/sol-macOS" 9 | INFOPLIST_FILE="Info.plist" 10 | INFOPLIST_DIR="${PROJECT_DIR}/${INFOPLIST_FILE}" 11 | 12 | PACKAGE_VERSION=$(cat package.json | grep version | head -1 | awk -F: '{ print $2 }' | sed 's/[\",]//g' | tr -d '[[:space:]]') 13 | 14 | BUILD_NUMBER=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${INFOPLIST_DIR}") 15 | BUILD_NUMBER=$(($BUILD_NUMBER + 1)) 16 | 17 | # Update plist with new values 18 | /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString ${PACKAGE_VERSION}" "${INFOPLIST_DIR}" 19 | /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" "${INFOPLIST_DIR}" 20 | 21 | git add . 22 | 23 | git commit -m "Bump version" 24 | 25 | git push 26 | 27 | git tag $PACKAGE_VERSION 28 | 29 | git push --tags -------------------------------------------------------------------------------- /src/app.tsx: -------------------------------------------------------------------------------- 1 | import './global.css' 2 | import 'config' 3 | import {RootContainer} from 'containers' 4 | import 'intl' 5 | import 'intl/locale-data/jsonp/en' 6 | import {root, StoreProvider} from 'store' 7 | import {vars} from 'nativewind' 8 | import {accentRgb} from 'mytailwind' 9 | import {LogBox, View} from 'react-native' 10 | 11 | const userTheme = vars({ 12 | '--color-accent': `${accentRgb?.r}, ${accentRgb?.g}, ${accentRgb?.b}`, 13 | }) 14 | 15 | LogBox.ignoreLogs(['Sending ']) 16 | 17 | export const App = () => { 18 | return ( 19 | 20 | 21 | 22 | 23 | 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /src/assets/Bluetooth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/Bluetooth.png -------------------------------------------------------------------------------- /src/assets/Cake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/Cake.png -------------------------------------------------------------------------------- /src/assets/Check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/Check.png -------------------------------------------------------------------------------- /src/assets/CheckCircleIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/CheckCircleIcon.png -------------------------------------------------------------------------------- /src/assets/ChevronDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/ChevronDown.png -------------------------------------------------------------------------------- /src/assets/ChevronLeft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/ChevronLeft.png -------------------------------------------------------------------------------- /src/assets/ChevronUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/ChevronUp.png -------------------------------------------------------------------------------- /src/assets/DarkModeIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/DarkModeIcon.png -------------------------------------------------------------------------------- /src/assets/DocumentIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/DocumentIcon.png -------------------------------------------------------------------------------- /src/assets/Giphy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/Giphy.png -------------------------------------------------------------------------------- /src/assets/GoogleMapsIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/GoogleMapsIcon.png -------------------------------------------------------------------------------- /src/assets/LockIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/LockIcon.png -------------------------------------------------------------------------------- /src/assets/SearchIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/SearchIcon.png -------------------------------------------------------------------------------- /src/assets/SettingsIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/SettingsIcon.png -------------------------------------------------------------------------------- /src/assets/SleepIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/SleepIcon.png -------------------------------------------------------------------------------- /src/assets/SolBlackSmall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/SolBlackSmall.png -------------------------------------------------------------------------------- /src/assets/SolWhiteSmall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/SolWhiteSmall.png -------------------------------------------------------------------------------- /src/assets/accessibility.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/accessibility.png -------------------------------------------------------------------------------- /src/assets/accounts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/accounts.png -------------------------------------------------------------------------------- /src/assets/airdrop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/airdrop.png -------------------------------------------------------------------------------- /src/assets/arrowLeftBlack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/arrowLeftBlack.png -------------------------------------------------------------------------------- /src/assets/arrowLeftWhite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/arrowLeftWhite.png -------------------------------------------------------------------------------- /src/assets/battery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/battery.png -------------------------------------------------------------------------------- /src/assets/brave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/brave.png -------------------------------------------------------------------------------- /src/assets/chrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/chrome.png -------------------------------------------------------------------------------- /src/assets/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/close.png -------------------------------------------------------------------------------- /src/assets/customIcons/Africa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Africa.png -------------------------------------------------------------------------------- /src/assets/customIcons/Airplane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Airplane.png -------------------------------------------------------------------------------- /src/assets/customIcons/Alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Alert.png -------------------------------------------------------------------------------- /src/assets/customIcons/Anchor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Anchor.png -------------------------------------------------------------------------------- /src/assets/customIcons/Android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Android.png -------------------------------------------------------------------------------- /src/assets/customIcons/Apple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Apple.png -------------------------------------------------------------------------------- /src/assets/customIcons/Asia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Asia.png -------------------------------------------------------------------------------- /src/assets/customIcons/Australia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Australia.png -------------------------------------------------------------------------------- /src/assets/customIcons/Bank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Bank.png -------------------------------------------------------------------------------- /src/assets/customIcons/BarChart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/BarChart.png -------------------------------------------------------------------------------- /src/assets/customIcons/Basket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Basket.png -------------------------------------------------------------------------------- /src/assets/customIcons/Battery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Battery.png -------------------------------------------------------------------------------- /src/assets/customIcons/Bike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Bike.png -------------------------------------------------------------------------------- /src/assets/customIcons/Bitcoin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Bitcoin.png -------------------------------------------------------------------------------- /src/assets/customIcons/Bolt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Bolt.png -------------------------------------------------------------------------------- /src/assets/customIcons/Book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Book.png -------------------------------------------------------------------------------- /src/assets/customIcons/Bookmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Bookmark.png -------------------------------------------------------------------------------- /src/assets/customIcons/Briefcase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Briefcase.png -------------------------------------------------------------------------------- /src/assets/customIcons/Brush.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Brush.png -------------------------------------------------------------------------------- /src/assets/customIcons/Bucket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Bucket.png -------------------------------------------------------------------------------- /src/assets/customIcons/Bug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Bug.png -------------------------------------------------------------------------------- /src/assets/customIcons/Calendar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Calendar.png -------------------------------------------------------------------------------- /src/assets/customIcons/Camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Camera.png -------------------------------------------------------------------------------- /src/assets/customIcons/Car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Car.png -------------------------------------------------------------------------------- /src/assets/customIcons/Cart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Cart.png -------------------------------------------------------------------------------- /src/assets/customIcons/Chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Chart.png -------------------------------------------------------------------------------- /src/assets/customIcons/Chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Chat.png -------------------------------------------------------------------------------- /src/assets/customIcons/Chip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Chip.png -------------------------------------------------------------------------------- /src/assets/customIcons/Clock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Clock.png -------------------------------------------------------------------------------- /src/assets/customIcons/Cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Cloud.png -------------------------------------------------------------------------------- /src/assets/customIcons/Compass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Compass.png -------------------------------------------------------------------------------- /src/assets/customIcons/Computer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Computer.png -------------------------------------------------------------------------------- /src/assets/customIcons/Cone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Cone.png -------------------------------------------------------------------------------- /src/assets/customIcons/CreditCard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/CreditCard.png -------------------------------------------------------------------------------- /src/assets/customIcons/Cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Cross.png -------------------------------------------------------------------------------- /src/assets/customIcons/Crown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Crown.png -------------------------------------------------------------------------------- /src/assets/customIcons/Cube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Cube.png -------------------------------------------------------------------------------- /src/assets/customIcons/Dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Dashboard.png -------------------------------------------------------------------------------- /src/assets/customIcons/Database.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Database.png -------------------------------------------------------------------------------- /src/assets/customIcons/DesignTools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/DesignTools.png -------------------------------------------------------------------------------- /src/assets/customIcons/Dna.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Dna.png -------------------------------------------------------------------------------- /src/assets/customIcons/Dollar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Dollar.png -------------------------------------------------------------------------------- /src/assets/customIcons/Education.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Education.png -------------------------------------------------------------------------------- /src/assets/customIcons/Ethereum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Ethereum.png -------------------------------------------------------------------------------- /src/assets/customIcons/Euro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Euro.png -------------------------------------------------------------------------------- /src/assets/customIcons/Europe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Europe.png -------------------------------------------------------------------------------- /src/assets/customIcons/Face.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Face.png -------------------------------------------------------------------------------- /src/assets/customIcons/FaceFlatSmile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/FaceFlatSmile.png -------------------------------------------------------------------------------- /src/assets/customIcons/FaceHeartEyes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/FaceHeartEyes.png -------------------------------------------------------------------------------- /src/assets/customIcons/FaceMask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/FaceMask.png -------------------------------------------------------------------------------- /src/assets/customIcons/FaceMonocle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/FaceMonocle.png -------------------------------------------------------------------------------- /src/assets/customIcons/FaceStarEyes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/FaceStarEyes.png -------------------------------------------------------------------------------- /src/assets/customIcons/FaceSunglasses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/FaceSunglasses.png -------------------------------------------------------------------------------- /src/assets/customIcons/FaceSurprise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/FaceSurprise.png -------------------------------------------------------------------------------- /src/assets/customIcons/FaceTired.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/FaceTired.png -------------------------------------------------------------------------------- /src/assets/customIcons/FaceTongue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/FaceTongue.png -------------------------------------------------------------------------------- /src/assets/customIcons/Favorite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Favorite.png -------------------------------------------------------------------------------- /src/assets/customIcons/Feather.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Feather.png -------------------------------------------------------------------------------- /src/assets/customIcons/Fire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Fire.png -------------------------------------------------------------------------------- /src/assets/customIcons/Flower.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Flower.png -------------------------------------------------------------------------------- /src/assets/customIcons/Folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Folder.png -------------------------------------------------------------------------------- /src/assets/customIcons/Gears.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Gears.png -------------------------------------------------------------------------------- /src/assets/customIcons/Github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Github.png -------------------------------------------------------------------------------- /src/assets/customIcons/Gitlab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Gitlab.png -------------------------------------------------------------------------------- /src/assets/customIcons/Heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Heart.png -------------------------------------------------------------------------------- /src/assets/customIcons/Home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Home.png -------------------------------------------------------------------------------- /src/assets/customIcons/Image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Image.png -------------------------------------------------------------------------------- /src/assets/customIcons/Inbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Inbox.png -------------------------------------------------------------------------------- /src/assets/customIcons/Joystick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Joystick.png -------------------------------------------------------------------------------- /src/assets/customIcons/Label.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Label.png -------------------------------------------------------------------------------- /src/assets/customIcons/Leaf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Leaf.png -------------------------------------------------------------------------------- /src/assets/customIcons/LightBulb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/LightBulb.png -------------------------------------------------------------------------------- /src/assets/customIcons/Link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Link.png -------------------------------------------------------------------------------- /src/assets/customIcons/Lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Lock.png -------------------------------------------------------------------------------- /src/assets/customIcons/Megaphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Megaphone.png -------------------------------------------------------------------------------- /src/assets/customIcons/MobilePhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/MobilePhone.png -------------------------------------------------------------------------------- /src/assets/customIcons/Mountain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Mountain.png -------------------------------------------------------------------------------- /src/assets/customIcons/NorthAmerica.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/NorthAmerica.png -------------------------------------------------------------------------------- /src/assets/customIcons/Page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Page.png -------------------------------------------------------------------------------- /src/assets/customIcons/Phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Phone.png -------------------------------------------------------------------------------- /src/assets/customIcons/PieChart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/PieChart.png -------------------------------------------------------------------------------- /src/assets/customIcons/Pin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Pin.png -------------------------------------------------------------------------------- /src/assets/customIcons/Present.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Present.png -------------------------------------------------------------------------------- /src/assets/customIcons/Project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Project.png -------------------------------------------------------------------------------- /src/assets/customIcons/Radar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Radar.png -------------------------------------------------------------------------------- /src/assets/customIcons/Recycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Recycle.png -------------------------------------------------------------------------------- /src/assets/customIcons/Robot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Robot.png -------------------------------------------------------------------------------- /src/assets/customIcons/Rocket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Rocket.png -------------------------------------------------------------------------------- /src/assets/customIcons/Search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Search.png -------------------------------------------------------------------------------- /src/assets/customIcons/Send.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Send.png -------------------------------------------------------------------------------- /src/assets/customIcons/Server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Server.png -------------------------------------------------------------------------------- /src/assets/customIcons/Shield.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Shield.png -------------------------------------------------------------------------------- /src/assets/customIcons/Ship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Ship.png -------------------------------------------------------------------------------- /src/assets/customIcons/Shop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Shop.png -------------------------------------------------------------------------------- /src/assets/customIcons/Sign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Sign.png -------------------------------------------------------------------------------- /src/assets/customIcons/Skull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Skull.png -------------------------------------------------------------------------------- /src/assets/customIcons/Sound.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Sound.png -------------------------------------------------------------------------------- /src/assets/customIcons/SouthAmerica.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/SouthAmerica.png -------------------------------------------------------------------------------- /src/assets/customIcons/Subscribe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Subscribe.png -------------------------------------------------------------------------------- /src/assets/customIcons/Sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Sun.png -------------------------------------------------------------------------------- /src/assets/customIcons/Tablet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Tablet.png -------------------------------------------------------------------------------- /src/assets/customIcons/Team.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Team.png -------------------------------------------------------------------------------- /src/assets/customIcons/TeeShirt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/TeeShirt.png -------------------------------------------------------------------------------- /src/assets/customIcons/Terminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Terminal.png -------------------------------------------------------------------------------- /src/assets/customIcons/ThumbsDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/ThumbsDown.png -------------------------------------------------------------------------------- /src/assets/customIcons/ThumbsUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/ThumbsUp.png -------------------------------------------------------------------------------- /src/assets/customIcons/Trash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Trash.png -------------------------------------------------------------------------------- /src/assets/customIcons/Tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Tree.png -------------------------------------------------------------------------------- /src/assets/customIcons/Umbrella.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Umbrella.png -------------------------------------------------------------------------------- /src/assets/customIcons/UnhappyFace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/UnhappyFace.png -------------------------------------------------------------------------------- /src/assets/customIcons/Users.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Users.png -------------------------------------------------------------------------------- /src/assets/customIcons/Video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Video.png -------------------------------------------------------------------------------- /src/assets/customIcons/Watch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/Watch.png -------------------------------------------------------------------------------- /src/assets/customIcons/World.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/customIcons/World.png -------------------------------------------------------------------------------- /src/assets/display.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/display.png -------------------------------------------------------------------------------- /src/assets/google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/google.png -------------------------------------------------------------------------------- /src/assets/google_translate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/google_translate.png -------------------------------------------------------------------------------- /src/assets/inbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/inbox.png -------------------------------------------------------------------------------- /src/assets/logoMinimal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/logoMinimal.png -------------------------------------------------------------------------------- /src/assets/logoMinimalWhite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/logoMinimalWhite.png -------------------------------------------------------------------------------- /src/assets/macosSettings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/macosSettings.png -------------------------------------------------------------------------------- /src/assets/network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/network.png -------------------------------------------------------------------------------- /src/assets/osp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/osp.png -------------------------------------------------------------------------------- /src/assets/safari.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/safari.png -------------------------------------------------------------------------------- /src/assets/shortcuts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/shortcuts.png -------------------------------------------------------------------------------- /src/assets/siri.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/siri.png -------------------------------------------------------------------------------- /src/assets/smallLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/smallLogo.png -------------------------------------------------------------------------------- /src/assets/sound.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/sound.png -------------------------------------------------------------------------------- /src/assets/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/star.png -------------------------------------------------------------------------------- /src/assets/starFilled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/starFilled.png -------------------------------------------------------------------------------- /src/assets/touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/touch.png -------------------------------------------------------------------------------- /src/assets/translate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/translate.png -------------------------------------------------------------------------------- /src/assets/users.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/users.png -------------------------------------------------------------------------------- /src/assets/wallpaper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/wallpaper.png -------------------------------------------------------------------------------- /src/assets/wifi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ospfranco/sol/d987ea2b02601920e0918b53211531793cf88727/src/assets/wifi.png -------------------------------------------------------------------------------- /src/colors.js: -------------------------------------------------------------------------------- 1 | const {solNative} = require('./lib/SolNative') 2 | 3 | const colors = { 4 | accent: solNative.accentColor, 5 | accentBg: `${solNative.accentColor}88`, 6 | accentBg2: `${solNative.accentColor}10`, 7 | } 8 | 9 | module.exports = colors 10 | -------------------------------------------------------------------------------- /src/components/BackButton.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx' 2 | import {useBoolean} from 'hooks' 3 | import React, {FC} from 'react' 4 | import { 5 | Image, 6 | TouchableOpacity, 7 | TouchableOpacityProps, 8 | View, 9 | useColorScheme, 10 | } from 'react-native' 11 | import {observer} from 'mobx-react-lite' 12 | import {useStore} from 'store' 13 | import {Assets} from 'assets' 14 | 15 | type Props = {} & TouchableOpacityProps 16 | 17 | export const BackButton: FC = observer(props => { 18 | const [hovered, hoverOn, hoverOff] = useBoolean() 19 | const colorScheme = useColorScheme() 20 | return ( 21 | 28 | 33 | 39 | 40 | 41 | ) 42 | }) 43 | -------------------------------------------------------------------------------- /src/components/BlurView.tsx: -------------------------------------------------------------------------------- 1 | import {requireNativeComponent, ViewStyle} from 'react-native' 2 | 3 | import {cssInterop} from 'nativewind' 4 | import {FC} from 'react' 5 | 6 | type BlurViewProps = { 7 | children?: any 8 | onLayout?: (e: any) => void 9 | style?: ViewStyle 10 | borderRadius?: number 11 | disabled?: boolean 12 | materialName?: 13 | | 'windowBackground' 14 | | 'menu' 15 | | 'sidebar' 16 | | 'header' 17 | | 'sheet' 18 | | 'popover' 19 | | 'hudWindow' 20 | | 'fullScreenUI' 21 | className?: string 22 | } 23 | 24 | export const BlurViewNative = requireNativeComponent('BlurView') 25 | 26 | export const BlurView: FC = props => { 27 | return ( 28 | 32 | ) 33 | } 34 | 35 | cssInterop(BlurView, { 36 | className: 'style', 37 | nativeStyleToProp: { 38 | // @ts-expect-error 39 | borderRadius: 'borderRadius', 40 | }, 41 | }) 42 | -------------------------------------------------------------------------------- /src/components/Dropdown.tsx: -------------------------------------------------------------------------------- 1 | import {Assets} from 'assets' 2 | import clsx from 'clsx' 3 | import {useBoolean} from 'hooks' 4 | import { 5 | Image, 6 | ScrollView, 7 | Text, 8 | TouchableOpacity, 9 | useColorScheme, 10 | View, 11 | ViewStyle, 12 | } from 'react-native' 13 | import {SelectableButton} from './SelectableButton' 14 | import {useState} from 'react' 15 | import {set} from 'lodash' 16 | 17 | interface Props { 18 | value: T 19 | style?: ViewStyle 20 | className?: string 21 | onValueChange: (t: T) => void 22 | options: Array<{ 23 | label: string 24 | value: T 25 | }> 26 | upward?: boolean 27 | } 28 | 29 | export const Dropdown = ({ 30 | value, 31 | style, 32 | options, 33 | onValueChange, 34 | upward = false, 35 | }: Props) => { 36 | const [isOpen, open, close] = useBoolean() 37 | const [isHovered, hoverOn, hoverOff] = useBoolean() 38 | const colorScheme = useColorScheme() 39 | const [x, setX] = useState(0) 40 | const [y, setY] = useState(0) 41 | const [width, setWidth] = useState(0) 42 | const [height, setHeight] = useState(0) 43 | 44 | return ( 45 | 46 | { 48 | const { 49 | x: layoutX, 50 | y: layoutY, 51 | width: layoutWidth, 52 | height: layoutHeight, 53 | } = e.nativeEvent.layout 54 | setX(layoutX) 55 | setY(layoutY) 56 | setHeight(layoutHeight) 57 | setWidth(layoutWidth) 58 | }} 59 | // @ts-expect-error 60 | onMouseEnter={hoverOn} 61 | onMouseLeave={hoverOff} 62 | enableFocusRing={false} 63 | onPress={() => { 64 | isOpen ? close() : open() 65 | }} 66 | className={clsx( 67 | `w-48 rounded justify-center items-center border flex-row py-1 border-neutral-300 dark:border-neutral-700`, 68 | { 69 | 'dark:bg-neutral-700': isHovered, 70 | 'border-accent': isOpen, 71 | }, 72 | )} 73 | style={style}> 74 | 75 | {options.find(o => o.value === value)?.label ?? ''} 76 | 77 | 84 | 85 | {isOpen && ( 86 | 97 | {options.map((o, i) => ( 98 | { 103 | onValueChange(o.value) 104 | close() 105 | }} 106 | /> 107 | ))} 108 | 109 | )} 110 | 111 | ) 112 | } 113 | -------------------------------------------------------------------------------- /src/components/Fade.tsx: -------------------------------------------------------------------------------- 1 | import React, {FC, useEffect, useRef} from 'react' 2 | import {Animated, ViewProps, ViewStyle} from 'react-native' 3 | 4 | interface Props extends ViewProps { 5 | visible: boolean 6 | style?: ViewStyle 7 | duration?: number 8 | } 9 | 10 | export const Fade: FC = ({ 11 | visible, 12 | style, 13 | children, 14 | duration = 100, 15 | ...rest 16 | }) => { 17 | const visibilityRef = useRef(new Animated.Value(0)) 18 | useEffect(() => { 19 | Animated.timing(visibilityRef.current, { 20 | toValue: visible ? 1 : 0, 21 | duration: duration, 22 | useNativeDriver: true, 23 | }).start() 24 | }, [visible]) 25 | 26 | const containerStyle = { 27 | opacity: visibilityRef.current.interpolate({ 28 | inputRange: [0, 1], 29 | outputRange: [0, 1], 30 | }), 31 | } 32 | 33 | const combinedStyle = [containerStyle, style] 34 | return ( 35 | 36 | {children} 37 | 38 | ) 39 | } 40 | -------------------------------------------------------------------------------- /src/components/FileIcon.tsx: -------------------------------------------------------------------------------- 1 | import {cssInterop} from 'nativewind' 2 | import {requireNativeComponent, ViewStyle} from 'react-native' 3 | 4 | const FileIconNative = requireNativeComponent<{ 5 | url: string 6 | style?: ViewStyle 7 | className?: string 8 | }>('FileIcon') 9 | 10 | export const FileIcon = (props: any) => { 11 | return 12 | } 13 | 14 | cssInterop(FileIcon, { 15 | className: 'style', 16 | }) 17 | -------------------------------------------------------------------------------- /src/components/GradientView.tsx: -------------------------------------------------------------------------------- 1 | import {cssInterop} from 'nativewind' 2 | import {requireNativeComponent, ViewStyle} from 'react-native' 3 | 4 | type GradientProps = { 5 | children?: any 6 | onLayout?: (e: any) => void 7 | style?: ViewStyle 8 | startColor: string 9 | endColor: string 10 | angle: number 11 | className?: string 12 | cornerRadius?: number 13 | } 14 | 15 | const GradientViewNative = requireNativeComponent('GradientView') 16 | 17 | export const GradientView = (props: GradientProps) => { 18 | return 19 | } 20 | 21 | cssInterop(GradientView, { 22 | className: 'style', 23 | }) 24 | -------------------------------------------------------------------------------- /src/components/Input.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx' 2 | import {useBoolean} from 'hooks' 3 | import {cssInterop} from 'nativewind' 4 | import React, {FC, MutableRefObject} from 'react' 5 | import {TextInput, TextInputProps, View, ViewStyle} from 'react-native' 6 | import colors from 'tailwindcss/colors' 7 | 8 | interface Props extends TextInputProps { 9 | inputRef?: MutableRefObject 10 | style?: ViewStyle 11 | inputStyle?: ViewStyle 12 | inputClassName?: string 13 | bordered?: boolean 14 | } 15 | 16 | export const Input: FC = ({ 17 | inputRef, 18 | style, 19 | inputStyle, 20 | bordered, 21 | autoFocus, 22 | inputClassName, 23 | ...props 24 | }) => { 25 | const [focused, focusOn, focusOff] = useBoolean(autoFocus) 26 | const [hovered, hoverOn, hoverOff] = useBoolean(false) 27 | return ( 28 | 41 | 53 | 54 | ) 55 | } 56 | 57 | cssInterop(Input, { 58 | className: 'style', 59 | inputClassName: 'inputStyle', 60 | nativeStyleToProp: { 61 | // @ts-expect-error 62 | borderRadius: 'borderRadius', 63 | }, 64 | }) 65 | -------------------------------------------------------------------------------- /src/components/Key.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx' 2 | import React, {FC} from 'react' 3 | import {Text, View, ViewStyle} from 'react-native' 4 | import colors from 'tailwindcss/colors' 5 | import {BlurView} from './BlurView' 6 | import {observer} from 'mobx-react-lite' 7 | import {useStore} from 'store' 8 | import {solNative} from 'lib/SolNative' 9 | 10 | interface IProps { 11 | title?: string 12 | symbol?: string 13 | primary?: boolean 14 | style?: ViewStyle 15 | brRounded?: boolean 16 | className?: string 17 | } 18 | 19 | export const Key: FC = observer( 20 | ({title, primary = false, style, symbol}) => { 21 | let store = useStore() 22 | 23 | return ( 24 | 25 | {!!title && ( 26 | 30 | {title.trim()} 31 | 32 | )} 33 | 34 | {!!symbol && ( 35 | 42 | 49 | {symbol} 50 | 51 | 52 | )} 53 | 54 | ) 55 | }, 56 | ) 57 | -------------------------------------------------------------------------------- /src/components/LoadingBar.tsx: -------------------------------------------------------------------------------- 1 | import {solNative} from 'lib/SolNative' 2 | import {observer} from 'mobx-react-lite' 3 | import React, {useEffect, useRef} from 'react' 4 | import {Animated, useColorScheme} from 'react-native' 5 | import {useStore} from 'store' 6 | 7 | export const LoadingBar = observer(() => { 8 | const store = useStore() 9 | const isDarkMode = store.ui.isDarkMode 10 | const animatedBorderRef = useRef(new Animated.Value(0)) 11 | const accentColor = solNative.accentColor 12 | 13 | useEffect(() => { 14 | Animated.timing(animatedBorderRef.current, { 15 | toValue: store.ui.isLoading ? 1 : 0, 16 | duration: 500, 17 | useNativeDriver: false, 18 | }).start() 19 | }, [store.ui.isLoading]) 20 | 21 | return ( 22 | 34 | ) 35 | }) 36 | -------------------------------------------------------------------------------- /src/components/MainInput.tsx: -------------------------------------------------------------------------------- 1 | import {observer} from 'mobx-react-lite' 2 | import {DevSettings, Image, TouchableOpacity, View} from 'react-native' 3 | import {TextInput} from 'react-native-macos' 4 | import {useStore} from 'store' 5 | import colors from 'tailwindcss/colors' 6 | import {Widget} from 'stores/ui.store' 7 | import {BackButton} from './BackButton' 8 | import {Assets} from 'assets' 9 | 10 | type Props = { 11 | placeholder?: string 12 | showBackButton?: boolean 13 | style?: any 14 | className?: string 15 | hideIcon?: boolean 16 | } 17 | 18 | export const MainInput = observer( 19 | ({ 20 | placeholder = 'Search for apps and commands...', 21 | showBackButton, 22 | hideIcon, 23 | }) => { 24 | const store = useStore() 25 | const isDarkMode = store.ui.isDarkMode 26 | const reloadApp = async () => { 27 | DevSettings.reload() 28 | } 29 | 30 | let leftButton = null 31 | if (showBackButton) { 32 | leftButton = ( 33 | { 35 | store.ui.setQuery('') 36 | store.ui.focusWidget(Widget.SEARCH) 37 | }} 38 | /> 39 | ) 40 | } 41 | 42 | if (!showBackButton) { 43 | leftButton = ( 44 | 45 | 49 | 50 | ) 51 | } 52 | 53 | if (hideIcon) { 54 | leftButton = null 55 | } 56 | 57 | return ( 58 | 59 | {leftButton} 60 | 71 | 72 | ) 73 | }, 74 | ) 75 | -------------------------------------------------------------------------------- /src/components/MyRadioButton.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx' 2 | import React, {FC} from 'react' 3 | import {Text, TouchableOpacity, View} from 'react-native' 4 | 5 | interface Props { 6 | label: string 7 | value: any 8 | onValueChange: (v: boolean) => void 9 | disabled?: boolean 10 | selected: boolean 11 | index?: number 12 | } 13 | 14 | export const MyRadioButton: FC = ({ 15 | label, 16 | value, 17 | onValueChange, 18 | selected, 19 | }) => { 20 | return ( 21 | onValueChange(value)}> 24 | 32 | {selected && } 33 | 34 | 38 | {label} 39 | 40 | 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /src/components/MySwitch.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx' 2 | import React, {FC} from 'react' 3 | import {TouchableOpacity, View} from 'react-native' 4 | 5 | interface Props { 6 | value: boolean 7 | onValueChange: (v: boolean) => void 8 | disabled?: boolean 9 | } 10 | 11 | export const MySwitch: FC = ({value, onValueChange, disabled}) => { 12 | return ( 13 | { 18 | onValueChange(!value) 19 | }} 20 | className={clsx(`w-7 h-[15px] rounded-full border`, { 21 | 'bg-[#4078D6] border-[#4078D6]': !!value, 22 | 'bg-neutral-300 dark:bg-neutral-700 border-neutral-200 dark:border-neutral-600': 23 | !value, 24 | })}> 25 | 34 | 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /src/components/PermissionsBar.tsx: -------------------------------------------------------------------------------- 1 | import {observer} from 'mobx-react-lite' 2 | import {FC} from 'react' 3 | import {Text, View} from 'react-native' 4 | import {useStore} from 'store' 5 | import {Key} from './Key' 6 | 7 | export const PermissionsBar: FC = observer(() => { 8 | let store = useStore() 9 | 10 | if (!!store.ui.query) { 11 | return null 12 | } 13 | 14 | if (store.ui.calendarAuthorizationStatus === 'notDetermined') { 15 | return ( 16 | 17 | 18 | Grant calendar access 19 | 20 | 21 | 22 | ) 23 | } 24 | 25 | if (!store.ui.isAccessibilityTrusted) { 26 | return ( 27 | 28 | 29 | Grant accessibility access 30 | 31 | 32 | 33 | ) 34 | } 35 | 36 | if (!store.ui.hasDismissedGettingStarted) { 37 | return ( 38 | 39 | 40 | Seems you just installed Sol, check out the getting started 41 | 42 | 43 | 44 | ) 45 | } 46 | 47 | return null 48 | }) 49 | -------------------------------------------------------------------------------- /src/components/SelectableButton.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx' 2 | import {useBoolean} from 'hooks' 3 | import React, {FC} from 'react' 4 | import { 5 | ImageSourcePropType, 6 | Text, 7 | TouchableOpacityProps, 8 | View, 9 | ViewStyle, 10 | Image, 11 | TouchableWithoutFeedback, 12 | } from 'react-native' 13 | 14 | interface SelectableButtonProps extends TouchableOpacityProps { 15 | selected: boolean 16 | title?: string 17 | style?: ViewStyle 18 | className?: string 19 | leftItem?: React.ReactNode 20 | rounded?: boolean 21 | icon?: ImageSourcePropType 22 | } 23 | 24 | export const SelectableButton: FC = ({ 25 | selected, 26 | title, 27 | style, 28 | leftItem, 29 | rounded, 30 | className, 31 | icon, 32 | ...props 33 | }) => { 34 | const [hovered, hoverOn, hoverOff] = useBoolean() 35 | return ( 36 | 42 | 52 | {!!icon && } 53 | {leftItem} 54 | {!!title && ( 55 | 59 | {title} 60 | 61 | )} 62 | 63 | 64 | ) 65 | } 66 | -------------------------------------------------------------------------------- /src/components/SolButton.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx' 2 | import {useBoolean} from 'hooks' 3 | import React, {FC} from 'react' 4 | import {Text, TouchableOpacity, TouchableOpacityProps} from 'react-native' 5 | 6 | interface Props extends TouchableOpacityProps { 7 | title: string 8 | } 9 | 10 | export const SolButton: FC = ({title, ...props}) => { 11 | const [isHovered, hoverOn, hoverOff] = useBoolean() 12 | return ( 13 | 25 | {title} 26 | 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /src/components/SystemPreferencesIcon.tsx: -------------------------------------------------------------------------------- 1 | import {solNative} from 'lib/SolNative' 2 | import React from 'react' 3 | import {ViewStyle} from 'react-native' 4 | import {FileIcon} from './FileIcon' 5 | 6 | export const SystemPreferencesIcon = ({style}: {style?: ViewStyle} = {}) => { 7 | return ( 8 | = 13 13 | ? '/System/Applications/System Settings.app' 14 | : '/System/Applications/System Preferences.app' 15 | } 16 | /> 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import {LogBox} from 'react-native' 2 | import Fuse, {IFuseOptions} from 'fuse.js' 3 | import * as Sentry from '@sentry/react-native' 4 | import {SentryDSN} from './env' 5 | 6 | LogBox.ignoreLogs(['Clipboard ', 'Component', 'Require cycle:']) 7 | 8 | export const FUSE_OPTIONS: IFuseOptions = { 9 | threshold: 0.15, 10 | ignoreLocation: true, 11 | findAllMatches: true, 12 | keys: [ 13 | {name: 'name', weight: 0.9}, 14 | {name: 'alias', weight: 0.1}, 15 | ], 16 | } 17 | 18 | if (!__DEV__) { 19 | Sentry.init({ 20 | dsn: SentryDSN, 21 | enableAppHangTracking: false, 22 | }) 23 | } else { 24 | Sentry.setUser({email: 'ospfranco@gmail.com'}) 25 | } 26 | -------------------------------------------------------------------------------- /src/containers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './root.container' 2 | -------------------------------------------------------------------------------- /src/containers/root.container.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx' 2 | import {FullCalendar} from 'components/FullCalendar' 3 | import {PermissionsBar} from 'components/PermissionsBar' 4 | import {observer} from 'mobx-react-lite' 5 | import {View} from 'react-native' 6 | import {useStore} from 'store' 7 | import {Widget} from 'stores/ui.store' 8 | import {ClipboardWidget} from 'widgets/clipboard.widget' 9 | import {CreateItemWidget} from 'widgets/createItem.widget' 10 | import {EmojisWidget} from 'widgets/emojis.widget' 11 | import {FileSearchWidget} from 'widgets/fileSearch.widget' 12 | import {OnboardingWidget} from 'widgets/onboarding.widget' 13 | import {ProcessesWidget} from 'widgets/processes.widget' 14 | import {ScratchpadWidget} from 'widgets/scratchpad.widget' 15 | import {SearchWidget} from 'widgets/search.widget' 16 | import {SettingsWidget} from 'widgets/settings.widget' 17 | import {TranslationWidget} from 'widgets/translation.widget' 18 | 19 | export const RootContainer = observer(() => { 20 | const store = useStore() 21 | const widget = store.ui.focusedWidget 22 | 23 | let subWindow = ( 24 | 0), 29 | })}> 30 | 31 | 32 | {!store.ui.query && store.ui.calendarEnabled && } 33 | 34 | 35 | 36 | ) 37 | 38 | if (widget === Widget.FILE_SEARCH) { 39 | subWindow = ( 40 | 41 | 42 | 43 | ) 44 | } 45 | if (widget === Widget.CLIPBOARD) { 46 | subWindow = ( 47 | 48 | 49 | 50 | ) 51 | } 52 | 53 | if (widget === Widget.EMOJIS) { 54 | subWindow = ( 55 | 56 | 57 | 58 | ) 59 | } 60 | 61 | if (widget === Widget.SCRATCHPAD) { 62 | subWindow = ( 63 | 64 | 65 | 66 | ) 67 | } 68 | 69 | if (widget === Widget.CREATE_ITEM) { 70 | subWindow = ( 71 | 72 | 73 | 74 | ) 75 | } 76 | 77 | if (widget === Widget.ONBOARDING) { 78 | subWindow = ( 79 | 80 | 81 | 82 | ) 83 | } 84 | 85 | if (widget === Widget.TRANSLATION) { 86 | subWindow = ( 87 | 88 | 89 | 90 | ) 91 | } 92 | 93 | if (widget === Widget.SETTINGS) { 94 | subWindow = ( 95 | 96 | 97 | 98 | ) 99 | } 100 | 101 | if (widget === Widget.PROCESSES) { 102 | subWindow = ( 103 | 104 | 105 | 106 | ) 107 | } 108 | 109 | return {subWindow} 110 | }) 111 | -------------------------------------------------------------------------------- /src/global.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | .fullWindow { 7 | height: 450; 8 | } 9 | .text { 10 | @apply text-neutral-800 dark:text-neutral-200; 11 | } 12 | .darker-text { 13 | @apply text-neutral-700 dark:text-neutral-400; 14 | } 15 | .highlight { 16 | @apply bg-accent dark:bg-accent-dark; 17 | } 18 | .border-window { 19 | @apply border-lightBorder dark:border-[#FFFFFF25]; 20 | } 21 | .border-color { 22 | @apply border-lightBorder dark:border-darkBorder; 23 | } 24 | .subBg { 25 | @apply bg-subBgLight dark:bg-subBgDark; 26 | } 27 | .bg-window { 28 | @apply dark:bg-[#00000066] bg-[#FFFFFFAA]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/globals.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | interface INativeEvent { 4 | id: string 5 | title?: string 6 | url?: string 7 | date: string 8 | endDate: string 9 | isAllDay: boolean 10 | notes: string 11 | color: string 12 | location: string 13 | eventStatus: number // 0 none, 1 confirmed, 2 tentative, 3 cancelled 14 | status: number // 0 none, 1 confirmed, 2 tentative, 3 cancelled 15 | declined: boolean 16 | eventLink?: string | null // Computed 17 | } 18 | 19 | declare module '*.png' { 20 | const value: import('react-native').ImageSourcePropType 21 | export default value 22 | } 23 | 24 | declare module '*.jpeg' { 25 | const value: import('react-native').ImageSourcePropType 26 | export default value 27 | } 28 | 29 | type CalendarAuthorizationStatus = 30 | | 'notDetermined' 31 | | 'restricted' 32 | | 'denied' 33 | | 'authorized' 34 | 35 | declare var global: { 36 | __SolProxy: { 37 | setHeight: (height: number) => void 38 | resetWindowSize: () => void 39 | hideWindow: () => void 40 | searchFiles: ( 41 | paths: string[], 42 | query: string, 43 | ) => {name: string; path: string; isFolder: boolean}[] 44 | requestCalendarAccess: () => Promise 45 | getCalendarAuthorizationStatus: () => CalendarAuthorizationStatus 46 | getEvents: () => INativeEvent[] 47 | ls: (path: string) => string[] 48 | exists: (path: string) => boolean 49 | readFile: (path: string) => string | null 50 | userName: () => string 51 | ps: () => string 52 | killProcess: (pid: string) => void 53 | getWifiPassword: () => {password: string; ssid: string} | null 54 | getWifiInfo: () => {ip: string | undefined} 55 | showWindow: () => void 56 | log: (message: string) => void 57 | } 58 | } 59 | 60 | interface Notification { 61 | title: string | undefined 62 | text: string | undefined 63 | app: string | undefined 64 | url: string | undefined 65 | date: number 66 | iden: string | undefined 67 | subt: string | undefined 68 | } 69 | 70 | interface IPeriod { 71 | id: number 72 | start: number 73 | end?: number 74 | } 75 | 76 | interface FileDescription { 77 | id: string 78 | filename: string 79 | path: string 80 | kind: string 81 | location: string 82 | } 83 | 84 | interface ITrackingProject { 85 | id: string 86 | name: string 87 | periods: IPeriod[] 88 | } 89 | 90 | type Item = { 91 | id: string 92 | icon?: string 93 | iconImage?: ImageURISource | number | ImageURISource[] 94 | IconComponent?: FC 95 | color?: string 96 | url?: string 97 | preventClose?: boolean 98 | type: ItemType 99 | name: string 100 | alias?: string 101 | subName?: string 102 | callback?: () => void 103 | metaCallback?: () => void 104 | isApplescript?: boolean 105 | text?: string 106 | isFavorite?: boolean // injected in UI array 107 | isRunning?: boolean // only apps have this 108 | bookmarkFolder?: null | string // only bookmarks have this 109 | } 110 | 111 | type OnboardingStep = 112 | | 'v1_start' 113 | | 'v1_shortcut' 114 | | 'v1_quick_actions' 115 | | 'v1_skipped' 116 | | 'v1_completed' 117 | -------------------------------------------------------------------------------- /src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useBoolean' 2 | -------------------------------------------------------------------------------- /src/hooks/useBoolean.ts: -------------------------------------------------------------------------------- 1 | import {useState} from 'react' 2 | 3 | export function useBoolean( 4 | initialValue: boolean = false, 5 | ): [boolean, () => void, () => void] { 6 | const [val, setVal] = useState(initialValue) 7 | const toTrue = () => { 8 | setVal(true) 9 | return true 10 | } 11 | const toFalse = () => { 12 | setVal(false) 13 | return true 14 | } 15 | 16 | return [val, toTrue, toFalse] 17 | } 18 | -------------------------------------------------------------------------------- /src/hooks/useFullSize.ts: -------------------------------------------------------------------------------- 1 | import {solNative} from 'lib/SolNative' 2 | import {useEffect, useRef} from 'react' 3 | import {useStore} from 'store' 4 | 5 | export function useFullSize() { 6 | // const store = useStore() 7 | // const oldHeight = useRef(store.ui.targetHeight) 8 | // useEffect(() => { 9 | // solNative.setWindowHeight(500) 10 | // return () => { 11 | // solNative.setWindowHeight(oldHeight.current) 12 | // } 13 | // }, []) 14 | } 15 | -------------------------------------------------------------------------------- /src/lib/calendar.ts: -------------------------------------------------------------------------------- 1 | import {CONSTANTS} from './constants' 2 | 3 | export const MEETING_PROVIDERS_URLS = [ 4 | 'https://us01web.zoom.us', 5 | 'https://us02web.zoom.us', 6 | 'https://us03web.zoom.us', 7 | 'https://us04web.zoom.us', 8 | 'https://us05web.zoom.us', 9 | 'https://us06web.zoom.us', 10 | 'https://meet.google.com', 11 | 'https://meet.ffmuc.net', 12 | 'https://meet.jit.si', 13 | 'https://teams.microsoft.com', 14 | ] 15 | 16 | export function extractMeetingLink( 17 | text?: string, 18 | location?: string, 19 | ): string | null { 20 | let link: string | null = null 21 | if (location) { 22 | const isLocationUrl = CONSTANTS.REGEX_VALID_URL.test(location) 23 | if (isLocationUrl) { 24 | link = location! 25 | } 26 | } 27 | 28 | if (!link && !!text) { 29 | link = 30 | text 31 | .replace(/\n/g, ' ') 32 | .replace('<', ' ') 33 | .replace('>', ' ') 34 | .split(' ') 35 | .filter(token => CONSTANTS.REGEX_VALID_URL.test(token)) 36 | .find(link => 37 | MEETING_PROVIDERS_URLS.some(baseUrl => link.includes(baseUrl)), 38 | ) ?? null 39 | } 40 | 41 | return link 42 | } 43 | -------------------------------------------------------------------------------- /src/lib/constants.ts: -------------------------------------------------------------------------------- 1 | export const CONSTANTS = { 2 | REGEX_VALID_URL: new RegExp( 3 | '^' + 4 | // protocol identifier 5 | '(?:(?:https?|ftp)://)' + 6 | // user:pass authentication 7 | '(?:\\S+(?::\\S*)?@)?' + 8 | '(?:' + 9 | // IP address exclusion 10 | // private & local networks 11 | '(?!(?:10|127)(?:\\.\\d{1,3}){3})' + 12 | '(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})' + 13 | '(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})' + 14 | // IP address dotted notation octets 15 | // excludes loopback network 0.0.0.0 16 | // excludes reserved space >= 224.0.0.0 17 | // excludes network & broacast addresses 18 | // (first & last IP address of each class) 19 | '(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])' + 20 | '(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}' + 21 | '(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))' + 22 | '|' + 23 | // host name 24 | '(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)' + 25 | // domain name 26 | '(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*' + 27 | // TLD identifier 28 | '(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))' + 29 | // TLD may end with dot 30 | '\\.?' + 31 | ')' + 32 | // port number 33 | '(?::\\d{2,5})?' + 34 | // resource path 35 | '(?:[/?#]\\S*)?' + 36 | '$', 37 | 'i', 38 | ), 39 | LESS_VALID_URL: new RegExp( 40 | '^' + 41 | // user:pass authentication 42 | '(?:\\S+(?::\\S*)?@)?' + 43 | '(?:' + 44 | // IP address exclusion 45 | // private & local networks 46 | '(?!(?:10|127)(?:\\.\\d{1,3}){3})' + 47 | '(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})' + 48 | '(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})' + 49 | // IP address dotted notation octets 50 | // excludes loopback network 0.0.0.0 51 | // excludes reserved space >= 224.0.0.0 52 | // excludes network & broacast addresses 53 | // (first & last IP address of each class) 54 | '(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])' + 55 | '(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}' + 56 | '(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))' + 57 | '|' + 58 | // host name 59 | '(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)' + 60 | // domain name 61 | '(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*' + 62 | // TLD identifier 63 | '(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))' + 64 | // TLD may end with dot 65 | '\\.?' + 66 | ')' + 67 | // port number 68 | '(?::\\d{2,5})?' + 69 | // resource path 70 | '(?:[/?#]\\S*)?' + 71 | '$', 72 | 'i', 73 | ), 74 | } 75 | -------------------------------------------------------------------------------- /src/lib/github.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | export interface Owner { 4 | login: string 5 | id: number 6 | node_id: string 7 | avatar_url: string 8 | gravatar_id: string 9 | url: string 10 | received_events_url: string 11 | type: string 12 | html_url: string 13 | followers_url: string 14 | following_url: string 15 | gists_url: string 16 | starred_url: string 17 | subscriptions_url: string 18 | organizations_url: string 19 | repos_url: string 20 | events_url: string 21 | site_admin: boolean 22 | } 23 | 24 | export interface License { 25 | key: string 26 | name: string 27 | url: string 28 | spdx_id: string 29 | node_id: string 30 | html_url: string 31 | } 32 | 33 | export interface GithubRepo { 34 | id: number 35 | node_id: string 36 | name: string 37 | full_name: string 38 | owner: Owner 39 | private: boolean 40 | html_url: string 41 | description: string 42 | fork: boolean 43 | url: string 44 | created_at: Date 45 | updated_at: Date 46 | pushed_at: Date 47 | homepage: string 48 | size: number 49 | stargazers_count: number 50 | watchers_count: number 51 | language: string 52 | forks_count: number 53 | open_issues_count: number 54 | master_branch: string 55 | default_branch: string 56 | score: number 57 | archive_url: string 58 | assignees_url: string 59 | blobs_url: string 60 | branches_url: string 61 | collaborators_url: string 62 | comments_url: string 63 | commits_url: string 64 | compare_url: string 65 | contents_url: string 66 | contributors_url: string 67 | deployments_url: string 68 | downloads_url: string 69 | events_url: string 70 | forks_url: string 71 | git_commits_url: string 72 | git_refs_url: string 73 | git_tags_url: string 74 | git_url: string 75 | issue_comment_url: string 76 | issue_events_url: string 77 | issues_url: string 78 | keys_url: string 79 | labels_url: string 80 | languages_url: string 81 | merges_url: string 82 | milestones_url: string 83 | notifications_url: string 84 | pulls_url: string 85 | releases_url: string 86 | ssh_url: string 87 | stargazers_url: string 88 | statuses_url: string 89 | subscribers_url: string 90 | subscription_url: string 91 | tags_url: string 92 | teams_url: string 93 | trees_url: string 94 | clone_url: string 95 | mirror_url: string 96 | hooks_url: string 97 | svn_url: string 98 | forks: number 99 | open_issues: number 100 | watchers: number 101 | has_issues: boolean 102 | has_projects: boolean 103 | has_pages: boolean 104 | has_wiki: boolean 105 | has_downloads: boolean 106 | archived: boolean 107 | disabled: boolean 108 | visibility: string 109 | license: License 110 | } 111 | 112 | export interface GithubQueryResult { 113 | total_count: number 114 | incomplete_results: boolean 115 | items: GithubRepo[] 116 | } 117 | 118 | export async function searchGithubRepos( 119 | query: string, 120 | token?: string | null, 121 | ): Promise { 122 | const headers: any = { 123 | Accept: 'application/vnd.github.v3+json', 124 | } 125 | 126 | if (!!token) { 127 | headers['Authorization'] = `token ${token}` 128 | } 129 | 130 | const res = await axios.get( 131 | `https://api.github.com/search/repositories?q=${query}&per_page=5`, 132 | {headers}, 133 | ) 134 | 135 | return res.data 136 | } 137 | -------------------------------------------------------------------------------- /src/lib/shorcuts.tsx: -------------------------------------------------------------------------------- 1 | import {Key} from 'components/Key' 2 | 3 | export const validShortcutTokensRegex = 4 | /^(cmd|control|option|command|shift|return|space|right|left|up|down|[a-ú]|[0-9])$/ 5 | 6 | export const defaultShortcuts = { 7 | // 'option+space': 'sol' 8 | resize_fullscreen: 'control+option+return', 9 | lock: 'command+option+q', 10 | resize_right_half: 'control+option+right', 11 | resize_left_half: 'control+option+left', 12 | resize_top_half: 'control+option+up', 13 | resize_bottom_half: 'control+option+down', 14 | resize_top_left: 'control+option+u', 15 | resize_top_right: 'control+option+i', 16 | resize_bottom_left: 'control+option+j', 17 | resize_bottom_right: 'control+option+k', 18 | move_to_next_screen: 'control+option+shift+right', 19 | move_to_previous_screen: 'control+option+shift+left', 20 | scratchpad: 'command+shift+space', 21 | emoji_picker: 'command+control+space', 22 | clipboard_manager: 'command+option+v', 23 | } 24 | 25 | export function renderToKeys(shortcut: string) { 26 | return shortcut.split('+').map((word, i) => { 27 | let char = '' 28 | switch (word) { 29 | case 'control': 30 | char = '⌃' 31 | break 32 | case 'option': 33 | char = '⌥' 34 | break 35 | case 'cmd': 36 | case 'command': 37 | char = '⌘' 38 | break 39 | case 'shift': 40 | char = '⇧' 41 | break 42 | case 'return': 43 | char = '↩' 44 | break 45 | case 'space': 46 | char = '␣' 47 | break 48 | case 'right': 49 | char = '→' 50 | break 51 | case 'left': 52 | char = '←' 53 | break 54 | case 'up': 55 | char = '↑' 56 | break 57 | case 'down': 58 | char = '↓' 59 | break 60 | default: 61 | char = word 62 | } 63 | return ( 64 | 65 | ) 66 | }) 67 | } 68 | -------------------------------------------------------------------------------- /src/lib/various.ts: -------------------------------------------------------------------------------- 1 | export async function sleep(ms: number): Promise { 2 | return new Promise(resolve => { 3 | setTimeout(() => { 4 | resolve() 5 | }, ms) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /src/lib/weather.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | export async function getWeather( 4 | apiKey: string, 5 | lat: string, 6 | lon: string, 7 | ): Promise<{ 8 | temp: number 9 | nextHourForecast: string 10 | } | null> { 11 | try { 12 | const res = await axios.get( 13 | `https://api.openweathermap.org/data/2.5/onecall?lat=${lat}&lon=${lon}&appid=${apiKey}&units=metric&exclude=minutely,daily,alerts`, 14 | ) 15 | 16 | return { 17 | temp: res.data.current.temp, 18 | nextHourForecast: res.data.hourly[0].weather[0].description, 19 | } 20 | } catch (e) { 21 | // console.warn('weather error', e) 22 | return null 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/mytailwind.ts: -------------------------------------------------------------------------------- 1 | import {solNative} from 'lib/SolNative' 2 | 3 | function hexToRgb(hex: string) { 4 | var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) 5 | return result 6 | ? { 7 | r: parseInt(result[1], 16), 8 | g: parseInt(result[2], 16), 9 | b: parseInt(result[3], 16), 10 | } 11 | : null 12 | } 13 | 14 | export const accentRgb = hexToRgb(solNative.accentColor) 15 | 16 | // config.theme.extend.colors.accent = solNative.accentColor 17 | // const accentRbg = hexToRgb(solNative.accentColor) 18 | // const accentDim = `rgba(${accentRbg?.r},${accentRbg?.g},${accentRbg?.b}, 0.6)` 19 | // const accentBg = `rgba(${accentRbg?.r},${accentRbg?.g},${accentRbg?.b}, 0.4)` 20 | 21 | // config.theme.extend.colors.accentDim = accentDim 22 | // config.theme.extend.colors.accentBg = accentBg 23 | -------------------------------------------------------------------------------- /src/store.ts: -------------------------------------------------------------------------------- 1 | import {createContext, useContext} from 'react' 2 | import {CalendarStore, createCalendarStore} from 'stores/calendar.store' 3 | import {ClipboardStore, createClipboardStore} from 'stores/clipboard.store' 4 | import {createKeystrokeStore, KeystrokeStore} from 'stores/keystroke.store' 5 | import {createUIStore, UIStore} from './stores/ui.store' 6 | import {ProcessesStore, createProcessesStore} from 'stores/processes.store' 7 | import {EmojiStore, createEmojiStore} from 'stores/emoji.store' 8 | 9 | export interface IRootStore { 10 | ui: UIStore 11 | clipboard: ClipboardStore 12 | keystroke: KeystrokeStore 13 | calendar: CalendarStore 14 | processes: ProcessesStore 15 | emoji: EmojiStore 16 | cleanUp: () => void 17 | } 18 | 19 | let createRootStore = (): IRootStore => { 20 | let store: any = {} 21 | 22 | store.ui = createUIStore(store) 23 | store.clipboard = createClipboardStore(store) 24 | store.keystroke = createKeystrokeStore(store) 25 | store.calendar = createCalendarStore(store) 26 | store.processes = createProcessesStore(store) 27 | store.emoji = createEmojiStore(store) 28 | ;(store as IRootStore).cleanUp = () => { 29 | store.ui.cleanUp() 30 | store.calendar.cleanUp() 31 | store.keystroke.cleanUp() 32 | store.clipboard.cleanUp() 33 | } 34 | 35 | return store 36 | } 37 | 38 | export let root = createRootStore() 39 | 40 | // @ts-expect-error hot is RN 41 | module.hot?.dispose(() => { 42 | root.cleanUp() 43 | }) 44 | 45 | export let StoreContext = createContext(root) 46 | export let StoreProvider = StoreContext.Provider 47 | export let useStore = () => useContext(StoreContext) 48 | -------------------------------------------------------------------------------- /src/stores/processes.store.tsx: -------------------------------------------------------------------------------- 1 | import {FUSE_OPTIONS} from 'config' 2 | import Fuse from 'fuse.js' 3 | import {solNative} from 'lib/SolNative' 4 | import {makeAutoObservable} from 'mobx' 5 | import {IRootStore} from 'store' 6 | 7 | export type Process = { 8 | id: number 9 | pid: number 10 | cpu: number 11 | mem: number 12 | type: 'prefPane' | 'app' | 'binary' 13 | path: string 14 | processName: string 15 | } 16 | 17 | export type ProcessesStore = ReturnType 18 | 19 | export const createProcessesStore = (root: IRootStore) => { 20 | let store = makeAutoObservable({ 21 | // ____ _ _ _ 22 | // / __ \| | | | | | 23 | // | | | | |__ ___ ___ _ ____ ____ _| |__ | | ___ ___ 24 | // | | | | '_ \/ __|/ _ \ '__\ \ / / _` | '_ \| |/ _ \/ __| 25 | // | |__| | |_) \__ \ __/ | \ V / (_| | |_) | | __/\__ \ 26 | // \____/|_.__/|___/\___|_| \_/ \__,_|_.__/|_|\___||___/ 27 | processes: [] as Process[], 28 | 29 | // _____ _ _ 30 | // / ____| | | | | 31 | // | | ___ _ __ ___ _ __ _ _| |_ ___ __| | 32 | // | | / _ \| '_ ` _ \| '_ \| | | | __/ _ \/ _` | 33 | // | |___| (_) | | | | | | |_) | |_| | || __/ (_| | 34 | // \_____\___/|_| |_| |_| .__/ \__,_|\__\___|\__,_| 35 | // | | 36 | // |_| 37 | get filteredProcesses() { 38 | if (!root.ui.query) return store.processes 39 | 40 | let results = new Fuse(store.processes, { 41 | ...FUSE_OPTIONS, 42 | keys: [{name: 'path', weight: 1}], 43 | // sortFn: (a, b) => { 44 | // return a.item['mem'] - b.item['mem'] 45 | // }, 46 | }) 47 | .search(root.ui.query) 48 | .map(r => r.item) 49 | 50 | return results 51 | }, 52 | 53 | // _ _ 54 | // /\ | | (_) 55 | // / \ ___| |_ _ ___ _ __ ___ 56 | // / /\ \ / __| __| |/ _ \| '_ \/ __| 57 | // / ____ \ (__| |_| | (_) | | | \__ \ 58 | // /_/ \_\___|\__|_|\___/|_| |_|___/ 59 | fetchProcesses: () => { 60 | const processesString = solNative.ps() 61 | 62 | const processes = processesString 63 | .split('\n') 64 | .map(line => { 65 | const defaultValue = ['', '', '', '', '', ''] 66 | const regex = /(\d+)\s+(\d+)\s+(\d+[.|,]\d+)\s+(\d+)\s+(.*)/ 67 | const [, id, pid, cpu, mem, path] = line.match(regex) ?? defaultValue 68 | const processName = path.match(/[^/]*[^/]*$/i)?.[0] ?? '' 69 | const isPrefPane = path.includes('.prefPane') 70 | const isApp = path.includes('.app/') 71 | 72 | return { 73 | id: parseInt(id), 74 | pid: parseInt(pid), 75 | cpu: parseFloat(cpu), 76 | mem: parseInt(mem), 77 | type: isPrefPane ? 'prefPane' : isApp ? 'app' : 'binary', 78 | path, 79 | processName, 80 | } as Process 81 | }) 82 | .filter(process => process.processName !== '') 83 | .sort((a, b) => { 84 | if (a.cpu != null && b.cpu != null) { 85 | return b.cpu - a.cpu 86 | } else { 87 | return 0 88 | } 89 | }) 90 | 91 | store.processes = processes 92 | }, 93 | }) 94 | 95 | return store 96 | } 97 | -------------------------------------------------------------------------------- /src/stores/storage.ts: -------------------------------------------------------------------------------- 1 | import {MMKV} from 'react-native-mmkv' 2 | 3 | export const storage = new MMKV() 4 | -------------------------------------------------------------------------------- /src/widgets/calendar.widget.tsx: -------------------------------------------------------------------------------- 1 | import {Key} from 'components/Key' 2 | import {solNative} from 'lib/SolNative' 3 | import {DateTime} from 'luxon' 4 | import {observer} from 'mobx-react-lite' 5 | import React, {FC, useEffect} from 'react' 6 | import {Text, TouchableOpacity, View} from 'react-native' 7 | import {useStore} from 'store' 8 | import {Widget} from 'stores/ui.store' 9 | 10 | export let CalendarWidget: FC = observer(() => { 11 | let store = useStore() 12 | let focused = store.ui.focusedWidget === Widget.CALENDAR 13 | 14 | useEffect(() => { 15 | if (focused) { 16 | solNative.turnOnHorizontalArrowsListeners() 17 | } else { 18 | solNative.turnOffHorizontalArrowsListeners() 19 | } 20 | }, [focused]) 21 | 22 | if (store.ui.calendarAuthorizationStatus === 'notDetermined') { 23 | return ( 24 | { 26 | solNative.requestCalendarAccess().then(() => { 27 | store.ui.getCalendarAccess() 28 | }) 29 | }}> 30 | 31 | Click to grant calendar access 32 | 33 | 34 | ) 35 | } 36 | 37 | if (!store.calendar.upcomingEvent) { 38 | return null 39 | } 40 | 41 | let lStart = DateTime.fromISO(store.calendar.upcomingEvent.date) 42 | let diff = Math.floor(lStart.diffNow().as('minutes')) 43 | 44 | return ( 45 | 46 | 52 | 53 | {store.calendar.upcomingEvent.title?.trim()} 54 | 55 | {diff > 0 ? ( 56 | 57 | 58 | in{' '} 59 | 60 | {diff} 61 | 62 | {' '} 63 | minutes 64 | 65 | 66 | ) : ( 67 | 68 | has started 69 | 70 | )} 71 | 72 | 77 | 78 | ) 79 | }) 80 | -------------------------------------------------------------------------------- /src/widgets/fileSearch.widget.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx' 2 | import {FileIcon} from 'components/FileIcon' 3 | import {Key} from 'components/Key' 4 | import {LoadingBar} from 'components/LoadingBar' 5 | import {MainInput} from 'components/MainInput' 6 | import {useFullSize} from 'hooks/useFullSize' 7 | import {observer} from 'mobx-react-lite' 8 | import {FC, useEffect, useRef} from 'react' 9 | import {FlatList, Text, View} from 'react-native' 10 | import {useStore} from 'store' 11 | 12 | interface Props { 13 | className?: string 14 | } 15 | 16 | export const FileSearchWidget: FC = observer(() => { 17 | useFullSize() 18 | 19 | const store = useStore() 20 | const data = store.ui.files 21 | const selectedIndex = store.ui.selectedIndex 22 | const listRef = useRef(null) 23 | 24 | useEffect(() => { 25 | if (data.length && store.ui.selectedIndex < data.length) { 26 | listRef.current?.scrollToIndex({ 27 | index: store.ui.selectedIndex, 28 | viewOffset: 80, 29 | }) 30 | } 31 | }, [selectedIndex]) 32 | 33 | return ( 34 | 35 | 36 | 37 | 38 | 39 | 46 | 47 | No items 48 | 49 | 50 | } 51 | getItemLayout={(_, index) => ({ 52 | length: 42, 53 | offset: 42 * index, 54 | index, 55 | })} 56 | keyExtractor={(_, index) => index.toString()} 57 | renderItem={({item, index}) => { 58 | const isActive = index === selectedIndex 59 | return ( 60 | 64 | 65 | {!!item.url && ( 66 | 67 | 68 | 69 | )} 70 | 71 | {item.name} 72 | 73 | 74 | {item.url!.slice(0, 45)} 75 | 76 | 77 | 78 | ) 79 | }} 80 | /> 81 | 82 | {data.length > 0 && ( 83 | 84 | Open Folder 85 | 86 | 87 | Open 88 | 89 | 90 | )} 91 | 92 | ) 93 | }) 94 | -------------------------------------------------------------------------------- /src/widgets/scratchpad.widget.tsx: -------------------------------------------------------------------------------- 1 | import {solNative} from 'lib/SolNative' 2 | import {observer} from 'mobx-react-lite' 3 | import {FC, useEffect} from 'react' 4 | import {TextInput, View} from 'react-native' 5 | import {useStore} from 'store' 6 | import colors from 'tailwindcss/colors' 7 | 8 | export const ScratchpadWidget: FC = observer(() => { 9 | let store = useStore() 10 | 11 | useEffect(() => { 12 | solNative.turnOffVerticalArrowsListeners() 13 | solNative.turnOffEnterListener() 14 | return () => { 15 | solNative.turnOnEnterListener() 16 | solNative.turnOnVerticalArrowsListeners() 17 | } 18 | }, []) 19 | 20 | return ( 21 | 22 | 33 | 34 | ) 35 | }) 36 | -------------------------------------------------------------------------------- /src/widgets/settings.widget.tsx: -------------------------------------------------------------------------------- 1 | import {useFullSize} from 'hooks/useFullSize' 2 | import {observer} from 'mobx-react-lite' 3 | import {FC, useState} from 'react' 4 | import {View} from 'react-native' 5 | import {About} from './settings/about' 6 | import {General} from './settings/general' 7 | import {Shortcuts} from './settings/shortcuts' 8 | import {Sidebar} from './settings/sidebar' 9 | import {Translate} from './settings/translate' 10 | 11 | type ITEM = 'ABOUT' | 'GENERAL' | 'TRANSLATE' | 'SHORTCUTS' 12 | 13 | export const SettingsWidget: FC = observer(() => { 14 | useFullSize() 15 | const [selected, setSelected] = useState('GENERAL') 16 | return ( 17 | 18 | 19 | 20 | {selected === 'GENERAL' && } 21 | {selected === 'ABOUT' && } 22 | {selected === 'SHORTCUTS' && } 23 | {selected === 'TRANSLATE' && } 24 | 25 | 26 | ) 27 | }) 28 | -------------------------------------------------------------------------------- /src/widgets/settings/about.tsx: -------------------------------------------------------------------------------- 1 | import {Assets} from 'assets' 2 | import {observer} from 'mobx-react-lite' 3 | import {Image, Linking, Text, TouchableOpacity, View} from 'react-native' 4 | import {useStore} from 'store' 5 | import packageInfo from '../../../package.json' 6 | 7 | export const About = observer(() => { 8 | const store = useStore() 9 | 10 | return ( 11 | 12 | 19 | 20 | Sol 21 | {packageInfo.version} 22 | 23 | by 24 | 25 | ospfranco 26 | 27 | 28 | { 31 | Linking.openURL('https://x.com/ospfranco') 32 | }}> 33 | Follow Me 34 | 35 | { 38 | Linking.openURL('https://sol.ospfranco.com/') 39 | }}> 40 | Website 41 | 42 | 43 | 44 | 45 | ) 46 | }) 47 | -------------------------------------------------------------------------------- /src/widgets/settings/shortcuts.tsx: -------------------------------------------------------------------------------- 1 | import {Icons} from 'assets' 2 | import clsx from 'clsx' 3 | import {FileIcon} from 'components/FileIcon' 4 | import {observer} from 'mobx-react-lite' 5 | import { 6 | FlatList, 7 | Image, 8 | Text, 9 | TextInput, 10 | TouchableOpacity, 11 | View, 12 | } from 'react-native' 13 | import {useStore} from 'store' 14 | import {ItemType} from 'stores/ui.store' 15 | 16 | export const Shortcuts = observer(() => { 17 | const store = useStore() 18 | const shortcuts = store.ui.shortcuts 19 | const validatedShortcuts = store.ui.validatedShortcuts 20 | let items = store.ui.items 21 | items.sort((a, b) => (a.name > b.name ? 1 : -1)) 22 | 23 | return ( 24 | 25 | 26 | 27 | System Wide Shortcuts 28 | 29 | Follow the syntax "[cmd + shift + option] + [letter]" 30 | 31 | 32 | 33 | Restore Defaults 34 | 35 | 36 | item.id} 40 | renderItem={({item, index}) => { 41 | return ( 42 | 49 | {!!item.url && } 50 | {item.type !== ItemType.CUSTOM && !!item.icon && ( 51 | {item.icon} 52 | )} 53 | 54 | {item.type === ItemType.CUSTOM && !!item.icon && ( 55 | 56 | {/* @ts-expect-error */} 57 | {Icons[item.icon] && ( 58 | 67 | )} 68 | 69 | )} 70 | {!!item.iconImage && ( 71 | 76 | )} 77 | {!!item.IconComponent && } 78 | {item.name} 79 | {!!shortcuts[item.id] ? ( 80 | validatedShortcuts[item.id].valid ? ( 81 | 82 | ) : ( 83 | 84 | ) 85 | ) : null} 86 | store.ui.setShorcut(item.id, t)} 91 | /> 92 | 93 | ) 94 | }} 95 | /> 96 | 97 | ) 98 | }) 99 | -------------------------------------------------------------------------------- /src/widgets/settings/sidebar.tsx: -------------------------------------------------------------------------------- 1 | import {Assets} from 'assets' 2 | import {BackButton} from 'components/BackButton' 3 | import {SelectableButton} from 'components/SelectableButton' 4 | import {View} from 'react-native' 5 | import {useStore} from 'store' 6 | import {Widget} from 'stores/ui.store' 7 | 8 | export const Sidebar = ({ 9 | selected, 10 | setSelected, 11 | }: { 12 | selected: string 13 | setSelected: (selected: string) => {} 14 | }) => { 15 | const store = useStore() 16 | 17 | return ( 18 | 19 | store.ui.focusWidget(Widget.SEARCH)} 21 | className="mb-2" 22 | /> 23 | setSelected('GENERAL')} 27 | title="General" 28 | /> 29 | setSelected('TRANSLATE')} 33 | title="Translation" 34 | /> 35 | setSelected('SHORTCUTS')} 40 | title="Shortcuts" 41 | /> 42 | setSelected('ABOUT')} 47 | title="About" 48 | /> 49 | 50 | ) 51 | } 52 | -------------------------------------------------------------------------------- /src/widgets/settings/translate.tsx: -------------------------------------------------------------------------------- 1 | import {Dropdown} from 'components/Dropdown' 2 | import {languages} from 'lib/languages' 3 | import {observer} from 'mobx-react-lite' 4 | import {Text, View} from 'react-native' 5 | import {useStore} from 'store' 6 | 7 | export const Translate = observer(() => { 8 | const store = useStore() 9 | 10 | return ( 11 | 12 | 13 | 14 | Select up to 3 languages for translation 15 | 16 | 17 | First language 18 | 19 | store.ui.setFirstTranslationLanguage(v as any)} 23 | options={Object.values(languages).map(v => ({ 24 | // @ts-expect-error 25 | label: `${v.name} ${v.flag ?? ''}`, 26 | value: v.code, 27 | }))} 28 | /> 29 | 30 | 31 | Second language 32 | 33 | store.ui.setSecondTranslationLanguage(v as any)} 37 | options={Object.values(languages).map((v, index) => ({ 38 | // @ts-expect-error 39 | label: `${v.name} ${v.flag ?? ''}`, 40 | value: v.code, 41 | }))} 42 | /> 43 | 44 | 45 | Third language 46 | 47 | store.ui.setThirdTranslationLanguage(v as any)} 51 | options={Object.values(languages).map(v => ({ 52 | // @ts-expect-error 53 | label: `${v.name} ${v.flag ?? ''}`, 54 | value: v.code, 55 | }))} 56 | /> 57 | 58 | 59 | 60 | ) 61 | }) 62 | -------------------------------------------------------------------------------- /src/widgets/translation.widget.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx' 2 | import {MainInput} from 'components/MainInput' 3 | import {useFullSize} from 'hooks/useFullSize' 4 | import {solNative} from 'lib/SolNative' 5 | import {languages} from 'lib/languages' 6 | import {observer} from 'mobx-react-lite' 7 | import {FC, useEffect} from 'react' 8 | import {StyleProp, Text, View, ViewStyle} from 'react-native' 9 | import {useStore} from 'store' 10 | 11 | interface Props { 12 | style?: StyleProp 13 | className?: string 14 | } 15 | 16 | export const TranslationWidget: FC = observer(({style}) => { 17 | const store = useStore() 18 | 19 | useEffect(() => { 20 | solNative.turnOnHorizontalArrowsListeners() 21 | 22 | return () => { 23 | solNative.turnOffHorizontalArrowsListeners() 24 | } 25 | }, []) 26 | 27 | const index = store.ui.selectedIndex 28 | 29 | return ( 30 | 31 | 32 | 33 | 34 | 35 | {!store.ui.translationResults.length && ( 36 | 37 | Translating... 38 | 39 | )} 40 | 41 | {!!store.ui.translationResults.length && ( 42 | 43 | 50 | 51 | {store.ui.translationResults[0]} 52 | 53 | 54 | {/* @ts-ignore */} 55 | {languages[store.ui.firstTranslationLanguage]?.flag ?? 56 | store.ui.firstTranslationLanguage} 57 | 58 | 59 | 60 | 67 | 68 | {store.ui.translationResults[1]} 69 | 70 | 71 | {/* @ts-ignore */} 72 | {languages[store.ui.secondTranslationLanguage]?.flag ?? 73 | store.ui.secondTranslationLanguage} 74 | 75 | 76 | 77 | {!!store.ui.thirdTranslationLanguage && ( 78 | 85 | 88 | {store.ui.translationResults[2]} 89 | 90 | 91 | {/* @ts-ignore */} 92 | {languages[store.ui.thirdTranslationLanguage]?.flag ?? 93 | store.ui.thirdTranslationLanguage} 94 | 95 | 96 | )} 97 | 98 | )} 99 | 100 | ) 101 | }) 102 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | 3 | module.exports = { 4 | content: ['./src/**/**.{tsx,ts}'], 5 | presets: [require('nativewind/preset')], 6 | theme: { 7 | minWidth: { 8 | 10: '10px', 9 | }, 10 | fontSize: { 11 | xxxs: '8px', 12 | xxs: '11px', 13 | xs: '12px', 14 | sm: '13px', 15 | base: '16px', 16 | lg: '18px', 17 | xl: '19px', 18 | '2xl': '20px', 19 | '3xl': '22px', 20 | '4xl': '26px', 21 | '5xl': '42px', 22 | '6xl': '52px', 23 | '7xl': '62px', 24 | '8xl': '128px', 25 | }, 26 | extend: { 27 | borderRadius: { 28 | xs: '1px', 29 | corner: '7px', 30 | xl: '14px', 31 | }, 32 | spacing: { 33 | 25: '108px', 34 | 26: '112px', 35 | }, 36 | colors: { 37 | darkWindowBorder: '#CCCCCC22', 38 | lightWindowBorder: '#FFFFFF', 39 | lightHighlight: 'rgba(0, 0, 0, .1)', 40 | darkHighlight: 'rgba(255, 255, 255, .07)', 41 | darkBorder: 'rgba(255, 255, 255, .1)', 42 | lightBorder: 'rgba(0, 0, 0, .1)', 43 | subBgDark: '#00000020', 44 | subBgLight: '#FFFFFF77', 45 | inputLight: '#00000010', 46 | inputDark: '#00000050', 47 | 'accent-strong': 'rgb(var(--color-accent) / .80)', 48 | accent: 'rgb(var(--color-accent) / .25)', 49 | 'accent-dark': 'rgb(var(--color-accent) / .14)', 50 | }, 51 | }, 52 | }, 53 | plugins: [ 54 | // Set a default value on the `:root` element 55 | ({addBase}) => addBase({':root': {'--color-accent': '255 0 0'}}), 56 | ], 57 | } 58 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@react-native/typescript-config/tsconfig.json", 3 | "compilerOptions": { 4 | "lib": ["ES2020"], 5 | "allowJs": true, 6 | "strict": true, 7 | "baseUrl": "./src", 8 | "jsx": "react-jsx", 9 | "allowSyntheticDefaultImports": true, 10 | "esModuleInterop": true, 11 | "useDefineForClassFields": true, 12 | "resolveJsonModule": true, 13 | "skipLibCheck": true 14 | } 15 | } 16 | --------------------------------------------------------------------------------