├── .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 | 
4 |
5 |
6 |
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 |
--------------------------------------------------------------------------------