├── .bun-version ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml ├── renovate.json ├── stale.yml └── workflows │ └── submit.yml ├── .gitignore ├── .gitmodules ├── .idea ├── $CACHE_FILE$ ├── .gitignore ├── CustomInspectionsConfig.xml ├── a-file-icon-web.iml ├── aws.xml ├── biome.xml ├── codeStyles │ └── codeStyleConfig.xml ├── codestream.xml ├── dictionaries ├── encodings.xml ├── git_toolbox_blame.xml ├── git_toolbox_prj.xml ├── highlightedFiles.xml ├── icon.png ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── js-inspections-ea-ultimate.xml ├── jsLibraryMappings.xml ├── markdown-navigator-enh.xml ├── markdown-navigator.xml ├── material_theme_project_new.xml ├── misc.xml ├── modules.xml ├── prettier.xml ├── saveactions_settings.xml └── vcs.xml ├── .nvmrc ├── LICENSE ├── README.md ├── SECURITY.md ├── assets ├── folderIconfont.ejs ├── global.css ├── icon.png ├── iconfont.ejs └── packs │ ├── angular.svg │ ├── angular2.svg │ ├── nest.svg │ ├── nextjs.svg │ ├── ngrx.svg │ ├── rails.svg │ ├── recoil.svg │ ├── redux.svg │ └── tests.svg ├── biome.json ├── bun.lockb ├── chrome └── manifest.json ├── firefox └── manifest.json ├── gulpfile.mjs ├── icon.png ├── icon128.png ├── icon16.png ├── icon32.png ├── icon48.png ├── package-lock.json ├── package.json ├── screens ├── 241807.png ├── 241808.png └── 241809.png ├── src ├── Global.styled.ts ├── assets │ └── logo.svg ├── associations │ ├── IconPack.ts │ ├── NodeLinkedList.ts │ ├── files.ts │ ├── folders.ts │ ├── types.ts │ └── utils.ts ├── common │ ├── Components │ │ ├── Checkbox.tsx │ │ ├── ColorPicker.tsx │ │ ├── IconButton.tsx │ │ ├── IconPacks.tsx │ │ └── Range.tsx │ ├── constants.ts │ ├── selectors.ts │ └── storage.ts ├── content.tsx ├── content │ ├── Fab.tsx │ ├── FabPopup.tsx │ ├── Panel │ │ └── Panel.tsx │ └── Settings │ │ ├── Form.tsx │ │ └── Settings.tsx ├── popup.tsx ├── popup │ ├── Loading │ │ └── Loading.tsx │ ├── Panel │ │ ├── Alert.tsx │ │ ├── Footer.tsx │ │ └── Panel.tsx │ └── Settings │ │ └── Settings.tsx └── providers │ ├── AbstractProvider.ts │ ├── ProviderFactory.ts │ ├── azure.ts │ ├── bitbucket.ts │ ├── codesandbox.ts │ ├── gitee.ts │ ├── github.ts │ ├── githubCodeView.ts │ ├── githubCodeViewTree.ts │ ├── gitlab.ts │ ├── pullRequests.ts │ └── search.ts ├── svgo.config.json ├── techstack.md ├── techstack.yml └── tsconfig.json /.bun-version: -------------------------------------------------------------------------------- 1 | 1.1.17 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: atom-material-themes-and-plugins # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # 5 | version: 2 6 | updates: 7 | - package-ecosystem: npm # See documentation for possible values 8 | directory: / # Location of package manifests 9 | schedule: 10 | interval: monthly 11 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "labels": [ 3 | "dependencies" 4 | ], 5 | "bumpVersion": "patch", 6 | "commitBodyTable": true, 7 | "prHourlyLimit": 6, 8 | "prConcurrentLimit": 6, 9 | "packageRules": [ 10 | { 11 | "description": "Automatically merge minor and patch-level updates", 12 | "matchUpdateTypes": [ 13 | "minor", 14 | "patch", 15 | "pin", 16 | "digest" 17 | ], 18 | "groupName": "Packages", 19 | "groupSlug": "packages", 20 | "semanticCommitType": "chore", 21 | "automerge": true, 22 | "automergeType": "branch", 23 | "semanticCommits": true, 24 | "schedule": "on friday and saturday", 25 | "labels": ["deps"] 26 | }, 27 | { 28 | "matchPackagePatterns": [ 29 | "eslint" 30 | ], 31 | "labels": [ 32 | "linting" 33 | ], 34 | "groupName": "eslint", 35 | "semanticCommitType": "chore", 36 | "automerge": true, 37 | "semanticCommits": true 38 | }, 39 | { 40 | "matchDepTypes": [ 41 | "optionalDependencies" 42 | ], 43 | "addLabels": [ 44 | "optional" 45 | ], 46 | "semanticCommitType": "chore", 47 | "automerge": true, 48 | "semanticCommits": true 49 | }, 50 | { 51 | "matchDepTypes": [ 52 | "peerDependencies" 53 | ], 54 | "addLabels": [ 55 | "peer" 56 | ], 57 | "matchUpdateTypes": [ 58 | "patch", 59 | "minor" 60 | ], 61 | "groupName": "peerDependencies (non-major)", 62 | "groupSlug": "peer-dependencies", 63 | "semanticCommitType": "chore", 64 | "automerge": true, 65 | "semanticCommits": true 66 | } 67 | ], 68 | "extends": [ 69 | "config:js-app", 70 | "packages:eslint" 71 | ] 72 | } 73 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | # Number of days of inactivity before an Issue or Pull Request becomes stale 4 | daysUntilStale: 1 5 | 6 | # Number of days of inactivity before an Issue or Pull Request with the stale 7 | # label is closed. 8 | # Set to false to disable. If disabled, issues still need to be closed manually, 9 | # but will remain marked as stale. 10 | daysUntilClose: 1 11 | 12 | # Issues or Pull Requests with these labels will never be considered stale. 13 | # Set to `[]` to disable 14 | exemptLabels: 15 | - dependencies 16 | - draft 17 | - WIP 18 | 19 | # Label to use when marking as stale 20 | staleLabel: stale 21 | 22 | # Comment to post when marking as stale. Set to `false` to disable 23 | markComment: > 24 | This PR has been automatically marked as stale because it has not had 25 | recent activity. It will be closed if no further activity occurs. Thank you 26 | for your contributions. 27 | 28 | # Comment to post when removing the stale label. 29 | # unmarkComment: > 30 | # Your comment here. 31 | 32 | # Comment to post when closing a stale Issue or Pull Request. 33 | closeComment: > 34 | This PR has been automatically closed because it has not had recent activity. 35 | You can reopen it by clicking on `Reopen pull request`. 36 | Thank you for your contributions. 37 | 38 | # Limit the number of actions per hour, from 1-30. Default is 30 39 | limitPerRun: 30 40 | 41 | # Limit to only `issues` or `pulls` 42 | only: pulls 43 | -------------------------------------------------------------------------------- /.github/workflows/submit.yml: -------------------------------------------------------------------------------- 1 | name: "Submit to Web Store" 2 | on: 3 | release: 4 | types: [published] 5 | workflow_dispatch: 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Clone repository 12 | uses: actions/checkout@v4 13 | 14 | - name: Clone Another Repository 15 | run: | 16 | git clone https://github.com/AtomMaterialUI/iconGenerator 17 | 18 | - name: Use Node.js 19 | uses: actions/setup-node@v4.0.2 20 | with: 21 | node-version: 22.x 22 | 23 | - uses: oven-sh/setup-bun@v2 24 | name: Setup Bun 25 | 26 | - name: Install 27 | run: bun install 28 | 29 | - name: Build chrome artifact 30 | run: bun run build -- --zip 31 | 32 | - name: Build firefox artifact 33 | run: bun run build -- --zip --target=firefox-mv3 34 | 35 | # - name: Create Release 36 | # uses: softprops/action-gh-release@v1 37 | # with: 38 | # generate_release_notes: true 39 | # body: | 40 | # ## Changelog 41 | # Please see the [changelog](https://github.com/microvoid/notion-ai-anywhere/blob/main/CHANGELOG.md) 42 | # files: | 43 | # build/chrome-mv3-prod.zip 44 | # build/firefox-mv3-prod.zip 45 | # build/edge-mv3-prod.zip 46 | 47 | - name: Browser Platform Publish 48 | uses: PlasmoHQ/bpp@v3 49 | with: 50 | keys: ${{ secrets.BPP_KEYS }} 51 | chrome-file: build/chrome-mv3-prod.zip 52 | firefox-file: build/firefox-mv3-prod.zip 53 | edge-file: build/edge-mv3-prod.zip 54 | verbose: true 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | iconGenerator 3 | public 4 | public/bundle.js 5 | public/bundle.js.map 6 | release 7 | release.zip 8 | web-ext-artifacts 9 | 10 | 11 | # Created by https://www.toptal.com/developers/gitignore/api/java,gradle,sass,node,git,visualstudiocode,macos,jetbrains+all 12 | # Edit at https://www.toptal.com/developers/gitignore?templates=java,gradle,sass,node,git,visualstudiocode,macos,jetbrains+all 13 | 14 | ### Git ### 15 | # Created by git for backups. To disable backups in Git: 16 | # $ git config --global mergetool.keepBackup false 17 | *.orig 18 | 19 | # Created by git when using merge tools for conflicts 20 | *.BACKUP.* 21 | *.BASE.* 22 | *.LOCAL.* 23 | *.REMOTE.* 24 | *_BACKUP_*.txt 25 | *_BASE_*.txt 26 | *_LOCAL_*.txt 27 | *_REMOTE_*.txt 28 | 29 | ### Java ### 30 | # Compiled class file 31 | *.class 32 | 33 | # Log file 34 | *.log 35 | 36 | # BlueJ files 37 | *.ctxt 38 | 39 | # Mobile Tools for Java (J2ME) 40 | .mtj.tmp/ 41 | 42 | # Package Files # 43 | *.jar 44 | *.war 45 | *.nar 46 | *.ear 47 | *.zip 48 | *.tar.gz 49 | *.rar 50 | 51 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 52 | hs_err_pid* 53 | 54 | ### JetBrains+all ### 55 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 56 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 57 | 58 | # User-specific stuff 59 | .idea/**/workspace.xml 60 | .idea/**/tasks.xml 61 | .idea/**/usage.statistics.xml 62 | .idea/**/dictionaries 63 | .idea/**/shelf 64 | 65 | # AWS User-specific 66 | .idea/**/aws.xml 67 | 68 | # Generated files 69 | .idea/**/contentModel.xml 70 | 71 | # Sensitive or high-churn files 72 | .idea/**/dataSources/ 73 | .idea/**/dataSources.ids 74 | .idea/**/dataSources.local.xml 75 | .idea/**/sqlDataSources.xml 76 | .idea/**/dynamic.xml 77 | .idea/**/uiDesigner.xml 78 | .idea/**/dbnavigator.xml 79 | 80 | # Gradle 81 | .idea/**/gradle.xml 82 | .idea/**/libraries 83 | 84 | # Gradle and Maven with auto-import 85 | # When using Gradle or Maven with auto-import, you should exclude module files, 86 | # since they will be recreated, and may cause churn. Uncomment if using 87 | # auto-import. 88 | # .idea/artifacts 89 | # .idea/compiler.xml 90 | # .idea/jarRepositories.xml 91 | # .idea/modules.xml 92 | # .idea/*.iml 93 | # .idea/modules 94 | # *.iml 95 | # *.ipr 96 | 97 | # CMake 98 | cmake-build-*/ 99 | 100 | # Mongo Explorer plugin 101 | .idea/**/mongoSettings.xml 102 | 103 | # File-based project format 104 | *.iws 105 | 106 | # IntelliJ 107 | out/ 108 | 109 | # mpeltonen/sbt-idea plugin 110 | .idea_modules/ 111 | 112 | # JIRA plugin 113 | atlassian-ide-plugin.xml 114 | 115 | # Cursive Clojure plugin 116 | .idea/replstate.xml 117 | 118 | # Crashlytics plugin (for Android Studio and IntelliJ) 119 | com_crashlytics_export_strings.xml 120 | crashlytics.properties 121 | crashlytics-build.properties 122 | fabric.properties 123 | 124 | # Editor-based Rest Client 125 | .idea/httpRequests 126 | 127 | # Android studio 3.1+ serialized cache file 128 | .idea/caches/build_file_checksums.ser 129 | 130 | 131 | *.iml 132 | modules.xml 133 | .idea/misc.xml 134 | *.ipr 135 | 136 | # Sonarlint plugin 137 | .idea/sonarlint 138 | 139 | ### macOS ### 140 | # General 141 | .DS_Store 142 | .AppleDouble 143 | .LSOverride 144 | 145 | # Icon must end with two \r 146 | Icon 147 | 148 | 149 | # Thumbnails 150 | ._* 151 | 152 | # Files that might appear in the root of a volume 153 | .DocumentRevisions-V100 154 | .fseventsd 155 | .Spotlight-V100 156 | .TemporaryItems 157 | .Trashes 158 | .VolumeIcon.icns 159 | .com.apple.timemachine.donotpresent 160 | 161 | # Directories potentially created on remote AFP share 162 | .AppleDB 163 | .AppleDesktop 164 | Network Trash Folder 165 | Temporary Items 166 | .apdisk 167 | 168 | ### Node ### 169 | # Logs 170 | logs 171 | npm-debug.log* 172 | yarn-debug.log* 173 | yarn-error.log* 174 | lerna-debug.log* 175 | .pnpm-debug.log* 176 | 177 | # Diagnostic reports (https://nodejs.org/api/report.html) 178 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 179 | 180 | # Runtime data 181 | pids 182 | *.pid 183 | *.seed 184 | *.pid.lock 185 | 186 | # Directory for instrumented libs generated by jscoverage/JSCover 187 | lib-cov 188 | 189 | # Coverage directory used by tools like istanbul 190 | coverage 191 | *.lcov 192 | 193 | # nyc test coverage 194 | .nyc_output 195 | 196 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 197 | .grunt 198 | 199 | # Bower dependency directory (https://bower.io/) 200 | bower_components 201 | 202 | # node-waf configuration 203 | .lock-wscript 204 | 205 | # Compiled binary addons (https://nodejs.org/api/addons.html) 206 | build/Release 207 | 208 | # Dependency directories 209 | node_modules/ 210 | jspm_packages/ 211 | 212 | # Snowpack dependency directory (https://snowpack.dev/) 213 | web_modules/ 214 | 215 | # TypeScript cache 216 | *.tsbuildinfo 217 | 218 | # Optional npm cache directory 219 | .npm 220 | 221 | # Optional eslint cache 222 | .eslintcache 223 | 224 | # Microbundle cache 225 | .rpt2_cache/ 226 | .rts2_cache_cjs/ 227 | .rts2_cache_es/ 228 | .rts2_cache_umd/ 229 | 230 | # Optional REPL history 231 | .node_repl_history 232 | 233 | # Output of 'npm pack' 234 | *.tgz 235 | 236 | # Yarn Integrity file 237 | .yarn-integrity 238 | 239 | # dotenv environment variables file 240 | .env 241 | .env.test 242 | .env.production 243 | 244 | # parcel-bundler cache (https://parceljs.org/) 245 | .cache 246 | .parcel-cache 247 | 248 | # Next.js build output 249 | .next 250 | out 251 | 252 | # Nuxt.js build / generate output 253 | .nuxt 254 | dist 255 | 256 | # Gatsby files 257 | .cache/ 258 | # Comment in the public line in if your project uses Gatsby and not Next.js 259 | # https://nextjs.org/blog/next-9-1#public-directory-support 260 | # public 261 | 262 | # vuepress build output 263 | .vuepress/dist 264 | 265 | # Serverless directories 266 | .serverless/ 267 | 268 | # FuseBox cache 269 | .fusebox/ 270 | 271 | # DynamoDB Local files 272 | .dynamodb/ 273 | 274 | # TernJS port file 275 | .tern-port 276 | 277 | # Stores VSCode versions used for testing VSCode extensions 278 | .vscode-test 279 | 280 | # yarn v2 281 | .yarn/cache 282 | .yarn/unplugged 283 | .yarn/build-state.yml 284 | .yarn/install-state.gz 285 | .pnp.* 286 | 287 | ### Sass ### 288 | .sass-cache/ 289 | *.css.map 290 | *.sass.map 291 | *.scss.map 292 | 293 | ### VisualStudioCode ### 294 | .vscode/* 295 | !.vscode/settings.json 296 | !.vscode/tasks.json 297 | !.vscode/launch.json 298 | !.vscode/extensions.json 299 | *.code-workspace 300 | 301 | # Local History for Visual Studio Code 302 | .history/ 303 | 304 | ### VisualStudioCode Patch ### 305 | # Ignore all local history of files 306 | .history 307 | .ionide 308 | 309 | ### Gradle ### 310 | .gradle 311 | build/ 312 | 313 | # Ignore Gradle GUI config 314 | gradle-app.setting 315 | 316 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 317 | !gradle-wrapper.jar 318 | 319 | # Cache of project 320 | .gradletasknamecache 321 | 322 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 323 | # gradle/wrapper/gradle-wrapper.properties 324 | 325 | ### Gradle Patch ### 326 | **/build/ 327 | 328 | # End of https://www.toptal.com/developers/gitignore/api/java,gradle,sass,node,git,visualstudiocode,macos,jetbrains+all 329 | 330 | # dependencies 331 | /.pnp 332 | .pnp.js 333 | 334 | # testing 335 | /coverage 336 | 337 | #cache 338 | .turbo 339 | .next 340 | .vercel 341 | 342 | # misc 343 | .DS_Store 344 | *.pem 345 | 346 | # debug 347 | npm-debug.log* 348 | yarn-debug.log* 349 | yarn-error.log* 350 | .pnpm-debug.log* 351 | 352 | 353 | # local env files 354 | .env* 355 | 356 | out/ 357 | build/ 358 | dist/ 359 | 360 | # plasmo - https://www.plasmo.com 361 | .plasmo 362 | 363 | # bpp - http://bpp.browser.market/ 364 | keys.json 365 | 366 | # typescript 367 | .tsbuildinfo 368 | 369 | public/icons -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "iconGenerator"] 2 | path = iconGenerator 3 | url = git@github.com:mallowigi/iconGenerator.git 4 | -------------------------------------------------------------------------------- /.idea/$CACHE_FILE$: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | BashSupport 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/CustomInspectionsConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Just do not write this. 7 | Forbidden t.xt 8 | Warning 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/a-file-icon-web.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /.idea/aws.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/biome.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/codestream.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /.idea/dictionaries: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/git_toolbox_blame.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/git_toolbox_prj.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/highlightedFiles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | -------------------------------------------------------------------------------- /.idea/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AtomMaterialUI/a-file-icon-web/71dcd49428a3f350f780f206210d9214c6513012/.idea/icon.png -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 190 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/js-inspections-ea-ultimate.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/jsLibraryMappings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/markdown-navigator-enh.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/markdown-navigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 21 | 22 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /.idea/material_theme_project_new.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 21 | 22 | 23 | 25 | 26 | 28 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/prettier.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/saveactions_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 22 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017-2020 Elior Boukhobza "Mallowigi" 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Atom Material File Icons Extension 2 | 3 | ![logo](https://raw.githubusercontent.com/mallowigi/a-file-icon-idea/master/src/main/resources/META-INF/pluginIcon.svg?sanitize=true) 4 | 5 | This extension is a port of Atom File Icons for use in Google Chrome and Mozilla Firefox. 6 | 7 | It replaces the standard file and folder icons with more suitable options related to file types, frameworks, or programming languages. 8 | 9 | 10 | 11 | * [Atom Material File Icons Extension](#atom-material-file-icons-extension) 12 | * [Chrome Extension](#chrome-extension) 13 | * [Firefox Extension](#firefox-extension) 14 | * [Edge Extension](#edge-extension) 15 | * [Supported Sites](#supported-sites) 16 | * [Features](#features) 17 | * [Icon Packs](#icon-packs) 18 | * [File Icons](#file-icons) 19 | * [Folder Icons](#folder-icons) 20 | * [Development](#development) 21 | * [Icon Generator](#icon-generator) 22 | * [Gulp Prepare](#gulp-prepare) 23 | * [Plasmo](#plasmo) 24 | * [Releasing](#releasing) 25 | * [Credits](#credits) 26 | 27 | 28 | 29 | ## Chrome Extension 30 | 31 | 32 | 33 | ## Firefox Extension 34 | 35 | 36 | 37 | ## Edge Extension 38 | 39 | 40 | 41 | ## Supported Sites 42 | 43 | This extension works on the following websites: 44 | 45 | - [GitHub](https://github.com) 46 | - [GitLab](https://gitlab.com) 47 | - [Bitbucket](https://bitbucket.org) 48 | - [Gitee](https://gitee.com) 49 | - [Azure](https://dev.azure.com) 50 | 51 | ## Features 52 | 53 | - Replaces **file icons** with their relevant logo icons 54 | - According to their extension (Java, PHP, Ruby...) 55 | - According to the framework (Android, NPM, RSpec...) 56 | - According to the program used with (Babel, Docker, CircleCI...) 57 | - Replaces **directories**: 58 | - With a common pattern: src, main, app, img, docs... 59 | - With a specific pattern: node_modules, .vscode, .git... 60 | - Settings: 61 | - Icon size: Change the icon size on the fly 62 | - Monochrome: Use monochrome icons 63 | - Folder Color: Change the color of regular folders 64 | - Icon Packs: Enable specific icon packs 65 | 66 | ### Icon Packs 67 | 68 | Icon Packs allow customization of icons based on common file patterns in selected frameworks like "controller", "service", "model", "view", 69 | etc. 70 | 71 | Available icon packs: 72 | 73 | - **Angular** 74 | - **NestJS** 75 | - **NextJS** 76 | - **NgRx** 77 | - **Rails** 78 | - **Redux** 79 | - **Recoil** 80 | - **Tests** 81 | 82 | ### File Icons 83 | 84 | ![File Icons](https://raw.githubusercontent.com/mallowigi/iconGenerator/master/assets/files.png) 85 | 86 | ### Folder Icons 87 | 88 | ![Folder Icons](https://raw.githubusercontent.com/mallowigi/iconGenerator/master/assets/folders.png) 89 | 90 | ## Development 91 | 92 | ### Icon Generator 93 | 94 | To build the extension, first clone the [iconGenerator](https://github.com/mallowigi/iconGenerator.git) repository containing all the icons. 95 | Set it up by following these steps: 96 | 97 | ```shell 98 | git clone https://github.com/mallowigi/iconGenerator.git 99 | npm install && cd iconGenerator && npm install 100 | npm run build 101 | ``` 102 | 103 | After running these commands, the `iconGenerator` folder will contain all the icons and the `icon_associations.json` 104 | and `folder_associations.json` files, which describe the associations between file patterns and icons. 105 | 106 | To rerun the generator: 107 | 108 | ```shell 109 | npm run convert 110 | ``` 111 | 112 | ### Gulp Prepare 113 | 114 | Next, you need to generate the index file that loads all icons as React components. This is done thanks to the `Gulp` task runner. 115 | 116 | ```shell 117 | npm run prepare 118 | ``` 119 | 120 | This process creates the `index.ts` file and places it in the `public/icons/files` and `public/icons/folders` directories. 121 | 122 | ### Plasmo 123 | 124 | The project uses [Plasmo](https://www.plasmo.com/) for building and running web extensions. Plasmo simplifies web extension development by 125 | supporting the latest web technologies like Webpack, TypeScript, React, Vite, etc. 126 | 127 | To start the development server in watch mode and generate an extension stub: 128 | 129 | ```shell 130 | npm run dev 131 | ``` 132 | 133 | Load the dev extension in your browser from `chrome://extensions` (or similar) using the `build/chrome-mv3-dev` directory. The extension 134 | will 135 | support hot reload and other features. 136 | 137 | ## Credits 138 | 139 | Special thanks to: 140 | 141 | - The [Material Theme UI plugin](https://www.material-theme.com) 142 | - [Atom File Icons](https://github.com/file-icons/atom) and [Sublime Text A File Icon](https://github.com/SublimeText/AFileIcon) 143 | - [Scientifics Study Vectors](https://www.svgrepo.com/svg/121720/atom) for the plugin icon 144 | - [File-Icons](https://github.com/file-icons/source/blob/master/charmap.md) 145 | - [FontAwesome 4.7.0](https://fontawesome.com/v4.7.0/cheatsheet/) 146 | - [MFixx](https://github.com/file-icons/MFixx/blob/master/charmap.md) 147 | - [Devicons](https://github.com/file-icons/DevOpicons/blob/master/charmap.md) 148 | - [Octicons](https://octicons.github.com/) 149 | - [Material Design Icons](https://materialdesignicons.com/) 150 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | | ------- | ------------------ | 10 | | 5.1.x | :white_check_mark: | 11 | | 5.0.x | :x: | 12 | | 4.0.x | :white_check_mark: | 13 | | < 4.0 | :x: | 14 | 15 | ## Reporting a Vulnerability 16 | 17 | Use this section to tell people how to report a vulnerability. 18 | 19 | Tell them where to go, how often they can expect to get an update on a 20 | reported vulnerability, what to expect if the vulnerability is accepted or 21 | declined, etc. 22 | -------------------------------------------------------------------------------- /assets/folderIconfont.ejs: -------------------------------------------------------------------------------- 1 | <% _.each(glyphs, function(glyph) { %> 2 | export * as folder_<%= glyph.name.replaceAll('-', '_') %> from 'data-text:./<%= glyph.name %>.svg'; 3 | <% }); %> 4 | -------------------------------------------------------------------------------- /assets/global.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --atom-icon-size: 20px; 3 | --atom-monochrome: none; 4 | --toggle-width: 2rem; 5 | } 6 | 7 | .octicon-pencil, 8 | .atomIcon, 9 | .atomIcon svg { 10 | width: var(--atom-icon-size); 11 | height: var(--atom-icon-size); 12 | filter: var(--atom-monochrome); 13 | } 14 | 15 | .atomIcon svg [data-themed] { 16 | fill: var(--atom-icon-color); 17 | } 18 | 19 | /* GitHub */ 20 | .js-details-container .Box-row { 21 | display: flex; 22 | align-items: center; 23 | gap: 8px; 24 | } 25 | 26 | /* GitLab */ 27 | .tree-item-link { 28 | display: flex !important; 29 | align-items: center !important; 30 | gap: 8px !important; 31 | } 32 | 33 | /*.rgh-quick-file-edit,*/ 34 | /*.Box-row > div:first-child {*/ 35 | /* display: flex;*/ 36 | /* width: auto !important;*/ 37 | /*}*/ 38 | 39 | .atomIcon.octicon { 40 | display: flex; 41 | } 42 | 43 | .tree-browser-result .atomIcon.octicon { 44 | display: inline-block; 45 | } 46 | 47 | /* GitHub CodeView */ 48 | .react-directory-filename-column { 49 | gap: 16px !important; 50 | } 51 | 52 | .ActionList-content { 53 | display: flex; 54 | align-items: center !important; 55 | gap: 8px; 56 | margin: 0.5em 0; 57 | } 58 | 59 | /* Azure */ 60 | .repos-files-hub-page .bolt-table-container .bolt-table-cell-content:not(.flex-column), 61 | .repos-files-hub-page .bolt-table-container .bolt-table-cell-content:not(.flex-column) > div { 62 | display: flex; 63 | align-items: center; 64 | gap: 8px; 65 | } 66 | 67 | #__bolt-splitter-fixed-pane0 .bolt-table-container .fabric-icon:not(.bolt-tree-expand-button)::before, 68 | #__bolt-splitter-fixed-pane1 .bolt-table-container .fabric-icon:not(.bolt-tree-expand-button)::before, 69 | #__bolt-splitter-fixed-pane2 .bolt-table-container .fabric-icon:not(.bolt-tree-expand-button)::before, 70 | .repos-files-hub-page .bolt-table-container .fabric-icon:not(.bolt-tree-expand-button)::before { 71 | display: none; 72 | } -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AtomMaterialUI/a-file-icon-web/71dcd49428a3f350f780f206210d9214c6513012/assets/icon.png -------------------------------------------------------------------------------- /assets/iconfont.ejs: -------------------------------------------------------------------------------- 1 | <% _.each(glyphs, function(glyph) { %> 2 | export * as file_<%= glyph.name.replaceAll(/-/g, '_') %> from 'data-text:./<%= glyph.name %>.svg'; 3 | <% }); %> 4 | -------------------------------------------------------------------------------- /assets/packs/angular.svg: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 | 29 | 30 | -------------------------------------------------------------------------------- /assets/packs/angular2.svg: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 | 29 | 30 | -------------------------------------------------------------------------------- /assets/packs/nest.svg: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 | 27 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /assets/packs/nextjs.svg: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 | 28 | 29 | -------------------------------------------------------------------------------- /assets/packs/ngrx.svg: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 | 28 | 29 | -------------------------------------------------------------------------------- /assets/packs/rails.svg: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 | 29 | 30 | -------------------------------------------------------------------------------- /assets/packs/recoil.svg: -------------------------------------------------------------------------------- 1 | 24 | 25 | 27 | 28 | 30 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /assets/packs/redux.svg: -------------------------------------------------------------------------------- 1 | 24 | 25 | 27 | 29 | 31 | 33 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /assets/packs/tests.svg: -------------------------------------------------------------------------------- 1 | 24 | 25 | 27 | 29 | 30 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.6.4/schema.json", 3 | "files": { 4 | "ignore": [ 5 | "node_modules", 6 | "public", 7 | "dist", 8 | "contracts/cache", 9 | "contracts/out", 10 | "contracts/generated.ts", 11 | "keys.json", 12 | "symbols/generated" 13 | ] 14 | }, 15 | "formatter": { 16 | "enabled": true, 17 | "formatWithErrors": false, 18 | "indentStyle": "space", 19 | "indentWidth": 2, 20 | "lineWidth": 80 21 | }, 22 | "linter": { 23 | "enabled": true, 24 | "rules": { 25 | "recommended": true, 26 | "complexity": { 27 | "noForEach": "off" 28 | }, 29 | "correctness": { 30 | "noUnusedVariables": "error", 31 | "useExhaustiveDependencies": "off" 32 | }, 33 | "performance": { 34 | "noAccumulatingSpread": "off", 35 | "noDelete": "off" 36 | }, 37 | "style": { 38 | "noNonNullAssertion": "off", 39 | "useShorthandArrayType": "error" 40 | }, 41 | "suspicious": { 42 | "noArrayIndexKey": "off", 43 | "noAssignInExpressions": "off", 44 | "noConfusingVoidType": "off", 45 | "noExplicitAny": "off" 46 | } 47 | } 48 | }, 49 | "javascript": { 50 | "formatter": { 51 | "quoteStyle": "single", 52 | "trailingComma": "all", 53 | "semicolons": "asNeeded" 54 | } 55 | }, 56 | "organizeImports": { 57 | "enabled": true 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AtomMaterialUI/a-file-icon-web/71dcd49428a3f350f780f206210d9214c6513012/bun.lockb -------------------------------------------------------------------------------- /chrome/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Elior Boukhobza (mallowigi)", 3 | "action": { 4 | "default_icon": "icon128.png", 5 | "default_title": "Atom File Icons" 6 | }, 7 | "content_scripts": [ 8 | { 9 | "css": [ 10 | "public/global.css" 11 | ], 12 | "js": [ 13 | "public/bundle.js" 14 | ], 15 | "matches": [ 16 | "http://github.com/*", 17 | "https://github.com/*", 18 | "http://bitbucket.org/*", 19 | "https://bitbucket.org/*", 20 | "http://gitlab.com/*", 21 | "https://gitlab.com/*", 22 | "http://gitee.com/*", 23 | "https://gitee.com/*" 24 | ] 25 | } 26 | ], 27 | "description": "Changes GitHub, GitLab, Gitee and BitBucket file icons according to their extension or framework", 28 | "homepage_url": "https://www.material-theme.com", 29 | "icons": { 30 | "128": "icon128.png", 31 | "16": "icon16.png", 32 | "32": "icon32.png", 33 | "48": "icon48.png" 34 | }, 35 | "manifest_version": 3, 36 | "name": "Atom File Icons Web", 37 | "host_permissions": [ 38 | "http://github.com/*", 39 | "https://github.com/*", 40 | "http://bitbucket.org/*", 41 | "https://bitbucket.org/*", 42 | "http://gitlab.com/*", 43 | "https://gitlab.com/*", 44 | "http://gitee.com/*", 45 | "https://gitee.com/*" 46 | ], 47 | "short_name": "Atom File Icons Web", 48 | "version": "7.0.0", 49 | "web_accessible_resources": [ 50 | { 51 | "resources": [ 52 | "public/icons/**/*" 53 | ], 54 | "matches": [ 55 | "http://github.com/*", 56 | "https://github.com/*", 57 | "http://bitbucket.org/*", 58 | "https://bitbucket.org/*", 59 | "http://gitlab.com/*", 60 | "https://gitlab.com/*", 61 | "http://gitee.com/*", 62 | "https://gitee.com/*" 63 | ] 64 | } 65 | ] 66 | } 67 | -------------------------------------------------------------------------------- /firefox/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Elior Boukhobza (mallowigi)", 3 | "browser_action": { 4 | "default_icon": "icon128.png", 5 | "default_title": "Atom File Icons" 6 | }, 7 | "content_scripts": [ 8 | { 9 | "css": [ 10 | "public/global.css" 11 | ], 12 | "js": [ 13 | "public/bundle.js" 14 | ], 15 | "matches": [ 16 | "http://github.com/*", 17 | "https://github.com/*", 18 | "http://bitbucket.org/*", 19 | "https://bitbucket.org/*", 20 | "http://gitlab.com/*", 21 | "https://gitlab.com/*", 22 | "http://gitee.com/*", 23 | "https://gitee.com/*" 24 | ] 25 | } 26 | ], 27 | "description": "Changes GitHub, GitLab, Gitee and BitBucket file icons according to their extension or framework", 28 | "homepage_url": "https://www.material-theme.com", 29 | "icons": { 30 | "128": "icon128.png", 31 | "16": "icon16.png", 32 | "32": "icon32.png", 33 | "48": "icon48.png" 34 | }, 35 | "manifest_version": 2, 36 | "name": "Atom File Icons Web", 37 | "permissions": [ 38 | "http://github.com/*", 39 | "https://github.com/*", 40 | "http://bitbucket.org/*", 41 | "https://bitbucket.org/*", 42 | "http://gitlab.com/*", 43 | "https://gitlab.com/*", 44 | "http://gitee.com/*", 45 | "https://gitee.com/*" 46 | ], 47 | "short_name": "Atom File Icons Web", 48 | "version": "7.0.0", 49 | "web_accessible_resources": [ 50 | "public/icons/**/*" 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /gulpfile.mjs: -------------------------------------------------------------------------------- 1 | import gulp from 'gulp'; 2 | import rename from 'gulp-rename'; 3 | import copy from 'gulp-copy'; 4 | import consolidate from 'gulp-consolidate'; 5 | import iconFont from 'gulp-iconfont'; 6 | import zip from 'gulp-zip'; 7 | 8 | gulp.task('icons', () => { 9 | return gulp.src('iconGenerator/assets/icons/files/*.svg') 10 | .pipe(copy('public', { prefix: 2 })) 11 | .pipe(iconFont({ 12 | fontName: 'icons', 13 | formats: ['eot', 'woff', 'ttf'], 14 | normalize: true, 15 | fontHeight: 1000, 16 | centerHorizontally: true, 17 | appendCodepoints: true, 18 | prependUnicode: false, 19 | })) 20 | .on('glyphs', function (glyphs, options) { 21 | gulp.src('assets/iconfont.ejs', {}) 22 | .pipe(consolidate('underscore', { glyphs: glyphs })) 23 | .pipe(rename('index.ts')) 24 | .pipe(gulp.dest('public/icons/files')); 25 | }); 26 | }); 27 | 28 | gulp.task('folders', () => { 29 | return gulp.src('iconGenerator/assets/icons/folders/*.svg') 30 | .pipe(copy('public', { prefix: 2 })) 31 | .pipe(iconFont({ 32 | fontName: 'folders', 33 | formats: ['eot', 'woff', 'ttf'], 34 | normalize: true, 35 | fontHeight: 1000, 36 | centerHorizontally: true, 37 | appendCodepoints: true, 38 | prependUnicode: false, 39 | })) 40 | .on('glyphs', function (glyphs, options) { 41 | gulp.src('assets/folderIconfont.ejs', {}) 42 | .pipe(consolidate('underscore', { glyphs: glyphs })) 43 | .pipe(rename('index.ts')) 44 | .pipe(gulp.dest('public/icons/folders')); 45 | }); 46 | }); 47 | 48 | gulp.task('assets', () => { 49 | return gulp.src([ 50 | 'iconGenerator/icon_associations.json', 51 | 'iconGenerator/folder_associations.json', 52 | 'assets/global.css', 53 | ]) 54 | .pipe(copy('public', { prefix: 1 })); 55 | }); 56 | 57 | gulp.task('prepare', gulp.series('icons', 'folders', 'assets')); 58 | 59 | gulp.task('copy', () => { 60 | return gulp.src(['*.*', '!release.zip', 'dist/*.css', 'public/**/*'], { allowEmpty: true }) 61 | .pipe(copy('release')); 62 | }); 63 | 64 | gulp.task('zip', () => { 65 | return gulp.src('release/**/*') 66 | .pipe(zip('release.zip')) 67 | .pipe(gulp.dest('.')); 68 | }); 69 | 70 | gulp.task('release', gulp.series('prepare', 'copy', 'zip')); 71 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AtomMaterialUI/a-file-icon-web/71dcd49428a3f350f780f206210d9214c6513012/icon.png -------------------------------------------------------------------------------- /icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AtomMaterialUI/a-file-icon-web/71dcd49428a3f350f780f206210d9214c6513012/icon128.png -------------------------------------------------------------------------------- /icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AtomMaterialUI/a-file-icon-web/71dcd49428a3f350f780f206210d9214c6513012/icon16.png -------------------------------------------------------------------------------- /icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AtomMaterialUI/a-file-icon-web/71dcd49428a3f350f780f206210d9214c6513012/icon32.png -------------------------------------------------------------------------------- /icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AtomMaterialUI/a-file-icon-web/71dcd49428a3f350f780f206210d9214c6513012/icon48.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "atom-material-icons-web", 3 | "version": "13.0.10", 4 | "description": "Atom Material Icons for Web", 5 | "author": "Elior Boukhobza ", 6 | "license": "MIT", 7 | "scripts": { 8 | "preconvert": "rimraf public", 9 | "convert": "npm --prefix iconGenerator run convert", 10 | "prepare": "gulp prepare", 11 | "prebuild": "bun run prepare", 12 | "build": "bun run plasmo:build", 13 | "predev": "bun run prepare", 14 | "dev": "bun run plasmo:dev", 15 | "plasmo:dev": "plasmo dev", 16 | "plasmo:build": "plasmo build --zip", 17 | "prerelease": "bun run build", 18 | "release": "gulp release", 19 | "webext": "web-ext build --overwrite-dest", 20 | "prefirefox": "bun run build && cp firefox/manifest.json .", 21 | "firefox": "bun run webext", 22 | "prechrome": "bun run build && cp chrome/manifest.json .", 23 | "chrome": "bun run release" 24 | }, 25 | "dependencies": { 26 | "@emotion/cache": "11.11.0", 27 | "@emotion/react": "11.11.4", 28 | "@emotion/styled": "11.11.5", 29 | "@plasmohq/storage": "^1.11.0", 30 | "biome": "^0.3.3", 31 | "js-yaml": "4.1.0", 32 | "lodash.capitalize": "^4.2.1", 33 | "lodash.debounce": "4.0.8", 34 | "plasmo": "0.88.0", 35 | "react": "18.3.1", 36 | "react-dom": "18.3.1", 37 | "select-dom": "9.0.0", 38 | "sirv": "2.0.4", 39 | "sirv-cli": "2.0.2" 40 | }, 41 | "devDependencies": { 42 | "@types/bun": "latest", 43 | "@ianvs/prettier-plugin-sort-imports": "4.3.0", 44 | "@types/chrome": "0.0.268", 45 | "@types/node": "20.14.9", 46 | "@types/react": "18.3.3", 47 | "@types/react-dom": "18.3.0", 48 | "polka": "0.5.2", 49 | "rimraf": "^5.0.7", 50 | "prettier": "3.3.2", 51 | "typescript": "5.5.2", 52 | "compression": "1.7.4", 53 | "gulp": "5.0.0", 54 | "gulp-clean": "0.4.0", 55 | "gulp-clean-css": "4.3.0", 56 | "gulp-consolidate": "0.2.0", 57 | "gulp-copy": "4.0.1", 58 | "gulp-iconfont": "11.0.1", 59 | "gulp-rename": "2.0.0", 60 | "gulp-sass": "5.1.0", 61 | "gulp-zip": "6.0.0", 62 | "node-fetch": "3.3.2", 63 | "underscore": "1.13.6", 64 | "web-ext": "8.2.0" 65 | }, 66 | "manifest": { 67 | "name": "Atom Material Icons", 68 | "description": "Atom Material Icons for Web (GitHub, BitBucket, Gitee, Azure and GitLab)", 69 | "author": "Elior Boukhobza (Mallowigi)", 70 | "homepage_url": "https://www.material-theme.com", 71 | "permissions": [ 72 | "storage" 73 | ], 74 | "browser_specific_settings": { 75 | "gecko": { 76 | "id": "{f0503c92-a634-43fd-912d-63c8fde00586}" 77 | } 78 | }, 79 | "host_permissions": [ 80 | "http://github.com/*", 81 | "https://github.com/*", 82 | "http://bitbucket.org/*", 83 | "https://bitbucket.org/*", 84 | "http://gitlab.com/*", 85 | "https://gitlab.com/*", 86 | "http://gitee.com/*", 87 | "https://gitee.com/*", 88 | "http://dev.azure.com/*", 89 | "https://dev.azure.com/*" 90 | ], 91 | "web_accessible_resources": [ 92 | { 93 | "resources": [ 94 | "assets/packs/*.svg" 95 | ], 96 | "matches": [ 97 | "http://github.com/*", 98 | "https://github.com/*", 99 | "http://bitbucket.org/*", 100 | "https://bitbucket.org/*", 101 | "http://gitlab.com/*", 102 | "https://gitlab.com/*", 103 | "http://gitee.com/*", 104 | "https://gitee.com/*", 105 | "http://dev.azure.com/*", 106 | "https://dev.azure.com/*" 107 | ] 108 | } 109 | ] 110 | }, 111 | "peerDependencies": { 112 | "typescript": "^5.0.0" 113 | }, 114 | "optionalDependencies": { 115 | "lodash.template": "4.5.0", 116 | "minimist": "1.2.8" 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /screens/241807.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AtomMaterialUI/a-file-icon-web/71dcd49428a3f350f780f206210d9214c6513012/screens/241807.png -------------------------------------------------------------------------------- /screens/241808.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AtomMaterialUI/a-file-icon-web/71dcd49428a3f350f780f206210d9214c6513012/screens/241808.png -------------------------------------------------------------------------------- /screens/241809.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AtomMaterialUI/a-file-icon-web/71dcd49428a3f350f780f206210d9214c6513012/screens/241809.png -------------------------------------------------------------------------------- /src/Global.styled.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react'; 2 | 3 | export const GlobalStyles = css` 4 | :host, 5 | :host :root, 6 | :root { 7 | --atom-bg: #263238; 8 | --atom-fg: #b0bec5; 9 | --atom-text: #607D8B; 10 | --atom-selectBg: #546E7A; 11 | --atom-selectFg: #FFFFFF; 12 | --atom-selFg: #FFFFFF; 13 | --atom-button: #2E3C43; 14 | --atom-secondBg: #32424A; 15 | --atom-lbg: #32424A; 16 | --atom-second: #32424A; 17 | --atom-disabled: #415967; 18 | --atom-contrast: #1E272C; 19 | --atom-dbg: #1E272C; 20 | --atom-active: #314549; 21 | --atom-border: #2A373E; 22 | --atom-hl: #425B67; 23 | --atom-tree: #546E7A70; 24 | --atom-notif: #1E272C; 25 | --atom-excluded: #2E3C43; 26 | --atom-excl: #2E3C43; 27 | --atom-yellow: #ffcb6b; 28 | --atom-attribute: #ffcb6b; 29 | --atom-green: #c3e88d; 30 | --atom-string: #c3e88d; 31 | --atom-cyan: #89ddff; 32 | --atom-operator: #89ddff; 33 | --atom-blue: #82aaff; 34 | --atom-function: #82aaff; 35 | --atom-purple: #c792ea; 36 | --atom-keyword: #c792ea; 37 | --atom-class: #ffcb6b; 38 | --atom-red: #f07178; 39 | --atom-tag: #f07178; 40 | --atom-red2: #ff5370; 41 | --atom-error: #ff5370; 42 | --atom-orange: #f78c6c; 43 | --atom-number: #f78c6c; 44 | --atom-orange2: #f78c6c; 45 | --atom-parameter: #f78c6c; 46 | --atom-gray: #546e7a; 47 | --atom-comment: #546e7a; 48 | --atom-silver: #eeffff; 49 | --atom-variable: #eeffff; 50 | --atom-black: #B0BEC5; 51 | --atom-primary: #78DCE8; 52 | --atom-primaryT: #78DCE890; 53 | --atom-accent: #009688; 54 | --atom-link: #80cbc4; 55 | --atom-accentT: #00968890; 56 | --atom-accent2: #80cbc4; 57 | --atom-accent2T: #80cbc490; 58 | --atom-links: #80cbc4; 59 | } 60 | 61 | *, 62 | *::before, 63 | *::after { 64 | -webkit-touch-callout: none; 65 | -webkit-user-select: none; 66 | user-select: none; 67 | padding: 0; 68 | margin: 0; 69 | box-sizing: border-box; 70 | } 71 | 72 | html, 73 | body { 74 | background: var(--atom-bg, #263238); 75 | overflow-y: auto; 76 | overflow-x: hidden; 77 | transition: all .5s; 78 | margin: 0; 79 | height: 100%; 80 | width: 100%; 81 | font-family: Roboto, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 82 | font-size: 14px; 83 | line-height: 1.5; 84 | color: var(--atom-fg); 85 | min-width: 450px; 86 | min-height: 300px; 87 | max-height: 800px; 88 | } 89 | 90 | h1, h2, h3, h4, h5, h6 { 91 | margin: 0 0 0.5em 0; 92 | font-weight: 400; 93 | line-height: 1.2; 94 | } 95 | 96 | h1 { 97 | font-size: 2em; 98 | } 99 | 100 | a { 101 | color: var(--atom-links, #80cbc4); 102 | } 103 | 104 | code { 105 | font-family: var(--font-family, "Operator Mono", menlo, inconsolata, monospace); 106 | font-size: var(--font-size, calc(1em - 2px)); 107 | } 108 | 109 | @media (min-width: 400px) { 110 | body { 111 | font-size: 16px; 112 | } 113 | } 114 | 115 | *::-webkit-scrollbar { 116 | width: 6px; 117 | } 118 | 119 | /* Track */ 120 | 121 | *::-webkit-scrollbar-track { 122 | background: var(--atom-bg); 123 | border-radius: 10px; 124 | } 125 | 126 | /* Handle */ 127 | 128 | *::-webkit-scrollbar-thumb { 129 | background: var(--atom-accent, #009688); 130 | border-radius: 10px; 131 | } 132 | 133 | /* Handle on hover */ 134 | 135 | *::-webkit-scrollbar-thumb:hover { 136 | background: var(--atom-accent, #009688); 137 | } 138 | 139 | input { 140 | background-color: var(--atom-contrast, #1e272c); 141 | color: var(--atom-text); 142 | transition: all .5s; 143 | } 144 | 145 | .comment { 146 | color: var(--atom-comment); 147 | transition: color .5s; 148 | } 149 | 150 | .keyword { 151 | color: var(--atom-keyword); 152 | transition: color .5s; 153 | } 154 | 155 | .function { 156 | color: var(--atom-function); 157 | transition: color .5s; 158 | } 159 | 160 | .punctuation { 161 | color: var(--atom-operator); 162 | transition: color .5s; 163 | } 164 | 165 | .string, 166 | .attr-value { 167 | color: var(--atom-string); 168 | transition: color .5s; 169 | } 170 | 171 | .parameter { 172 | color: var(--atom-parameter); 173 | transition: color .5s; 174 | } 175 | 176 | .number { 177 | color: var(--atom-number); 178 | transition: color .5s; 179 | } 180 | 181 | .attr-name { 182 | color: var(--atom-attribute); 183 | transition: color .5s; 184 | } 185 | 186 | .tag { 187 | color: var(--atom-tag); 188 | transition: color .5s; 189 | } 190 | `; 191 | -------------------------------------------------------------------------------- /src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 31 | 32 | 34 | 39 | 41 | 46 | 49 | 55 | 58 | 63 | 65 | 70 | 72 | 77 | 79 | 85 | 87 | 93 | 95 | 100 | 103 | 108 | 110 | 114 | 116 | 120 | 125 | 126 | 137 | 138 | 139 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /src/associations/IconPack.ts: -------------------------------------------------------------------------------- 1 | export enum IconPack { 2 | TESTS = 'tests', 3 | ANGULAR2 = 'angular2', 4 | ANGULAR = 'angular', 5 | NEST = 'nest', 6 | NEXTJS = 'nextjs', 7 | NGRX = 'ngrx', 8 | RAILS = 'rails', 9 | RECOIL = 'recoil', 10 | REDUX = 'redux', 11 | } 12 | 13 | export const iconPacks = [ 14 | { 15 | name: 'Angular (Old)', 16 | id: IconPack.ANGULAR, 17 | icon: 'angular.svg', 18 | }, 19 | { 20 | name: 'Angular', 21 | id: IconPack.ANGULAR2, 22 | icon: 'angular2.svg', 23 | }, 24 | { 25 | name: 'NestJS', 26 | id: IconPack.NEST, 27 | icon: 'nest.svg', 28 | }, 29 | { 30 | name: 'NextJS', 31 | id: IconPack.NEXTJS, 32 | icon: 'nextjs.svg', 33 | }, 34 | { 35 | name: 'NGRX', 36 | id: IconPack.NGRX, 37 | icon: 'ngrx.svg', 38 | }, 39 | { 40 | name: 'Rails', 41 | id: IconPack.RAILS, 42 | icon: 'rails.svg', 43 | }, 44 | { 45 | name: 'Recoil', 46 | id: IconPack.RECOIL, 47 | icon: 'recoil.svg', 48 | }, 49 | { 50 | name: 'Redux', 51 | id: IconPack.REDUX, 52 | icon: 'redux.svg', 53 | }, 54 | { 55 | name: 'Tests', 56 | id: IconPack.TESTS, 57 | icon: 'tests.svg', 58 | }, 59 | ]; 60 | 61 | export type IconPacks = Record; 62 | -------------------------------------------------------------------------------- /src/associations/NodeLinkedList.ts: -------------------------------------------------------------------------------- 1 | type LinkedNode = { 2 | value: T, 3 | next: LinkedNode | null; 4 | } 5 | 6 | export class NodeLinkedList { 7 | private nodes: LinkedNode[] = []; 8 | 9 | get size(): number { 10 | return this.nodes.length; 11 | } 12 | 13 | get head(): LinkedNode | null { 14 | return this.size ? this.nodes[0] : null; 15 | } 16 | 17 | get tail(): LinkedNode | null { 18 | return this.size ? this.nodes[this.size - 1] : null; 19 | } 20 | 21 | insertAt(index, value) { 22 | const previousNode = this.nodes[index - 1] || null; 23 | const nextNode = this.nodes[index] || null; 24 | const node = { value, next: nextNode }; 25 | 26 | if (previousNode) { 27 | previousNode.next = node; 28 | } 29 | this.nodes.splice(index, 0, node); 30 | } 31 | 32 | unshift(value) { 33 | this.insertAt(0, value); 34 | } 35 | 36 | put(value) { 37 | this.insertAt(this.size, value); 38 | } 39 | 40 | at(index) { 41 | return this.nodes[index]; 42 | } 43 | 44 | remove(index): LinkedNode[] { 45 | const previousNode = this.nodes[index - 1]; 46 | const nextNode = this.nodes[index + 1] || null; 47 | 48 | if (previousNode) { 49 | previousNode.next = nextNode; 50 | } 51 | 52 | return this.nodes.splice(index, 1); 53 | } 54 | 55 | clear() { 56 | this.nodes = []; 57 | } 58 | 59 | find(fn): T | null { 60 | let node = this.head; 61 | while (node) { 62 | if (node.value && fn(node.value)) { 63 | return node.value; 64 | } 65 | node = node.next; 66 | } 67 | return null; 68 | } 69 | 70 | * [Symbol.iterator]() { 71 | yield* this.nodes; 72 | } 73 | } -------------------------------------------------------------------------------- /src/associations/files.ts: -------------------------------------------------------------------------------- 1 | import iconAssociations from '../../public/icon_associations.json'; 2 | import { NodeLinkedList } from '~associations/NodeLinkedList'; 3 | import type { FileIconAssociation } from '~associations/types'; 4 | import { IconType } from '~associations/types'; 5 | import * as icons from '../../public/icons/files/index'; 6 | import type { IconPack, IconPacks } from '~associations/IconPack'; 7 | 8 | type RawFileIconAssociation = { 9 | fileNames: string; 10 | icon: string; 11 | name: string; 12 | pattern: string; 13 | iconType: 'FILE'; 14 | priority: string; 15 | iconColor: string; 16 | url?: string; 17 | iconPack?: IconPack; 18 | } 19 | 20 | const cache = new NodeLinkedList(); 21 | 22 | export function clearCache() { 23 | cache.clear(); 24 | } 25 | 26 | const DEFAULT: FileIconAssociation = { 27 | fileNames: '', 28 | name: 'Default', 29 | pattern: new RegExp(''), 30 | iconColor: '78909C', 31 | iconType: IconType.FILE, 32 | enabled: true, 33 | priority: 1, 34 | icon: '/icons/files/default.svg', 35 | }; 36 | 37 | const makeFileIconAssociation = (json: RawFileIconAssociation): FileIconAssociation => { 38 | if (!json) { 39 | return DEFAULT; 40 | } 41 | 42 | return { 43 | enabled: true, 44 | fileNames: json.fileNames, 45 | icon: json.icon, 46 | iconColor: json.iconColor, 47 | iconType: IconType.FILE, 48 | name: json.name, 49 | pattern: new RegExp(json.pattern), 50 | priority: parseInt(json.priority, 10), 51 | url: json.url, 52 | iconPack: json.iconPack, 53 | }; 54 | }; 55 | 56 | function findAssociation(regexps: RawFileIconAssociation[], name: string, iconPacks?: IconPacks) { 57 | return regexps 58 | .filter(assoc => !assoc.iconPack || iconPacks?.[assoc.iconPack.toLowerCase()]) 59 | .find(assoc => new RegExp(assoc.pattern).test(name)); 60 | } 61 | 62 | function searchInCache(name: string): FileIconAssociation | null { 63 | return cache.find((assoc: FileIconAssociation) => { 64 | return assoc.priority >= 100 && assoc.pattern.test(name); 65 | }); 66 | } 67 | 68 | export function getAssociation(name: string, iconPacks?: IconPacks): FileIconAssociation { 69 | const regexps = iconAssociations.associations.associations.regex.map(i => i.value) as RawFileIconAssociation[]; 70 | 71 | const cached = searchInCache(name); 72 | if (cached) { 73 | return cached; 74 | } 75 | 76 | let association = findAssociation(regexps, name, iconPacks); 77 | let foundAssoc = makeFileIconAssociation(association); 78 | if (foundAssoc) { 79 | cache.put(foundAssoc); 80 | } 81 | return foundAssoc ?? DEFAULT; 82 | } 83 | 84 | export function getFileIconName(assoc: FileIconAssociation = DEFAULT) { 85 | return `${assoc.icon 86 | .replace('/icons/files/', '') 87 | .replace('.svg', '') 88 | .replace('-', '_') 89 | }`; 90 | } 91 | 92 | export function getFileIcon(iconName: string, isDark = false) { 93 | const darkIcon = icons[`file_${iconName}${isDark ? '_dark' : ''}`]; 94 | return darkIcon ?? icons[`file_${iconName}`]; 95 | } 96 | -------------------------------------------------------------------------------- /src/associations/folders.ts: -------------------------------------------------------------------------------- 1 | import iconAssociations from '../../public/folder_associations.json'; 2 | import * as icons from '../../public/icons/folders/index'; 3 | import type { FolderIconAssociation } from '~associations/types'; 4 | import { IconType } from '~associations/types'; 5 | import { NodeLinkedList } from '~associations/NodeLinkedList'; 6 | 7 | type RawFolderIconAssociation = { 8 | folderNames: string; 9 | icon: string; 10 | name: string; 11 | pattern: string; 12 | iconType: 'FOLDER'; 13 | priority: string; 14 | folderColor: string; 15 | folderIconColor: string; 16 | } 17 | 18 | const cache = new NodeLinkedList(); 19 | 20 | const DEFAULT: FolderIconAssociation = { 21 | enabled: true, 22 | folderNames: '', 23 | folderColor: '78909C', 24 | folderIconColor: 'B0BEC5', 25 | icon: '/default.svg', 26 | iconType: IconType.FOLDER, 27 | name: 'Default', 28 | pattern: new RegExp(''), 29 | priority: 1, 30 | }; 31 | 32 | const makeFolderIconAssociation = (json: RawFolderIconAssociation): FolderIconAssociation => { 33 | if (!json) { 34 | return DEFAULT; 35 | } 36 | return { 37 | folderNames: json.folderNames, 38 | enabled: true, 39 | icon: json.icon, 40 | name: json.name, 41 | pattern: new RegExp(json.pattern), 42 | iconType: IconType.FOLDER, 43 | priority: parseInt(json.priority, 10), 44 | folderIconColor: json.folderIconColor, 45 | folderColor: json.folderColor, 46 | }; 47 | }; 48 | 49 | function searchInCache(name: string): FolderIconAssociation | null { 50 | return cache.find((assoc: FolderIconAssociation) => { 51 | return assoc.priority >= 100 && assoc.pattern.test(name); 52 | }); 53 | } 54 | 55 | function findAssociation() { 56 | return iconAssociations.associations.associations.regex.map(i => i.value) as RawFolderIconAssociation[]; 57 | } 58 | 59 | export function getFolderAssociation(name: string): FolderIconAssociation { 60 | const regexps = findAssociation(); 61 | 62 | const cached = searchInCache(name); 63 | if (cached) { 64 | return cached; 65 | } 66 | 67 | let foundAssoc = makeFolderIconAssociation(regexps.find(assoc => new RegExp(assoc.pattern.replace('^', ''), 'g') 68 | .test(name.toLowerCase()))); 69 | if (foundAssoc) { 70 | cache.put(foundAssoc); 71 | } 72 | return foundAssoc ?? DEFAULT; 73 | } 74 | 75 | export function getFolderIconName(assoc: FolderIconAssociation = DEFAULT) { 76 | return `${assoc.icon 77 | .replace('/', '') 78 | .replace('.svg', '') 79 | .replace('-', '_')}`; 80 | } 81 | 82 | export function getFolderIcon(iconName: string) { 83 | return icons[`folder_${iconName}`]; 84 | } 85 | -------------------------------------------------------------------------------- /src/associations/types.ts: -------------------------------------------------------------------------------- 1 | import type { IconPack, IconPacks } from '~associations/IconPack'; 2 | 3 | export enum IconType { 4 | FILE = 'FILE', 5 | FOLDER = 'FOLDER' 6 | } 7 | 8 | type IconAssociation = { 9 | enabled: boolean; 10 | icon: string; 11 | name: string; 12 | pattern: RegExp; 13 | iconType: IconType; 14 | priority: number; 15 | } 16 | 17 | export type FileIconAssociation = IconAssociation & { 18 | fileNames: string; 19 | iconType: IconType.FILE; 20 | iconColor: string; 21 | url?: string; 22 | iconPack?: IconPack; 23 | } 24 | 25 | export type FolderIconAssociation = IconAssociation & { 26 | folderNames: string; 27 | folderColor: string; 28 | folderIconColor: string; 29 | iconType: IconType.FOLDER; 30 | } 31 | 32 | export type AtomSettings = { 33 | isMonochrome: boolean; 34 | iconSize: number; 35 | accentColor: string; 36 | iconPacks: IconPacks; 37 | } -------------------------------------------------------------------------------- /src/associations/utils.ts: -------------------------------------------------------------------------------- 1 | export const removeSize = (svg: string) => svg 2 | .replace('width="16px"', ``) 3 | .replace('height="16px"', ``); 4 | 5 | export const wrapSvg = async (svg: string, styles = '', className = '') => 6 | `${svg}`; 7 | 8 | export const withColor = (svg: string, color: string) => svg.replaceAll(/fill="(.*)"/g, `fill="${color}"`); 9 | 10 | export const changeCssVariable = (property: string, value: string) => { 11 | document.documentElement.style.setProperty(property, value); 12 | }; -------------------------------------------------------------------------------- /src/common/Components/Checkbox.tsx: -------------------------------------------------------------------------------- 1 | import styled from '@emotion/styled'; 2 | 3 | const Section = styled.div` 4 | display: block; 5 | //margin: 1rem auto; 6 | 7 | label { 8 | display: block; 9 | text-align: left; 10 | margin-bottom: 0.5em; 11 | } 12 | 13 | input { 14 | display: block; 15 | background-image: none; 16 | } 17 | 18 | input[type='checkbox'] { 19 | filter: none; 20 | position: absolute; 21 | opacity: 0; 22 | background: var(--atom-bg); 23 | 24 | & + label { 25 | position: relative; 26 | cursor: pointer; 27 | padding: 0; 28 | 29 | &:before { 30 | position: relative; 31 | content: ''; 32 | margin-right: 10px; 33 | display: inline-block; 34 | vertical-align: text-top; 35 | width: 16px; 36 | height: 16px; 37 | background: var(--atom-bg); 38 | border: 2px solid var(--atom-accent); 39 | border-radius: 4px; 40 | } 41 | } 42 | 43 | &:hover { 44 | & + label:before { 45 | background: var(--atom-contrast); 46 | } 47 | } 48 | 49 | &:focus { 50 | & + label:before { 51 | box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.12); 52 | } 53 | } 54 | 55 | &:checked { 56 | & + label:before { 57 | background: var(--atom-accent); 58 | color: var(--atom-bg); 59 | } 60 | 61 | & + label:after { 62 | content: ''; 63 | position: absolute; 64 | left: 3px; 65 | top: 38%; 66 | background: var(--atom-bg); 67 | width: 2px; 68 | height: 2px; 69 | box-shadow: 2px 0 0 var(--atom-bg), 4px 0 0 var(--atom-bg), 4px -2px 0 var(--atom-bg), 4px -4px 0 var(--atom-bg), 70 | 4px -6px 0 var(--atom-bg), 4px -8px 0 var(--atom-bg); 71 | transform: rotate(45deg); 72 | } 73 | } 74 | 75 | &:disabled { 76 | & + label { 77 | color: #b8b8b8; 78 | cursor: auto; 79 | } 80 | 81 | & + label:before { 82 | box-shadow: none; 83 | opacity: 0.6; 84 | } 85 | } 86 | } 87 | `; 88 | 89 | interface Props { 90 | id: string; 91 | isChecked: boolean; 92 | setChecked: (isChecked: boolean) => void; 93 | text: string; 94 | icon?: string; 95 | } 96 | 97 | export const Checkbox = ({ id, isChecked, setChecked, text, icon }: Props) => { 98 | const handleChange = (e: React.ChangeEvent) => { 99 | setChecked(e.target.checked); 100 | }; 101 | 102 | return ( 103 |
104 | 110 | 114 |
115 | ); 116 | }; 117 | -------------------------------------------------------------------------------- /src/common/Components/ColorPicker.tsx: -------------------------------------------------------------------------------- 1 | import styled from '@emotion/styled'; 2 | 3 | const Section = styled.section` 4 | display: flex; 5 | justify-content: space-between; 6 | margin: 0 auto; 7 | gap: 1em; 8 | 9 | input[type="color"] { 10 | -webkit-appearance: none; 11 | width: 32px; 12 | height: 32px; 13 | border-radius: 50%; 14 | } 15 | 16 | input[type="color"]::-webkit-color-swatch-wrapper { 17 | padding: 0; 18 | border-radius: 50%; 19 | } 20 | 21 | input[type="color"]::-webkit-color-swatch { 22 | border-color: var(--atom-border); 23 | border-radius: 50%; 24 | } 25 | `; 26 | 27 | const Badge = styled.span<{ color: string }>` 28 | display: inline-block; 29 | background-color: ${({ color }) => color ?? 'var(--atom-accent)'}; 30 | color: white; 31 | padding: 4px; 32 | border-radius: 4px; 33 | `; 34 | 35 | export const ColorPicker = ({ id, value, setValue, text }) => { 36 | const handleChange = (e) => { 37 | setValue(e.target.value); 38 | }; 39 | 40 | return ( 41 |
42 | 43 |
44 | 50 |
51 |
52 | ); 53 | }; 54 | -------------------------------------------------------------------------------- /src/common/Components/IconButton.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react'; 2 | import styled from '@emotion/styled'; 3 | 4 | interface ButtonProps { 5 | isChecked: boolean; 6 | } 7 | 8 | const Button = styled.button(({ isChecked }: ButtonProps) => css` 9 | display: flex; 10 | border: ${isChecked ? '2px solid var(--atom-accent)' : '2px solid transparent'}; 11 | border-radius: 4px; 12 | background-color: var(--atom-button); 13 | color: var(--atom-foreground); 14 | padding: 8px; 15 | flex-direction: column; 16 | justify-content: center; 17 | align-items: center; 18 | transition: background-color 0.2s ease; 19 | width: 100%; 20 | height: 100%; 21 | font-size: 12px; 22 | cursor: pointer; 23 | 24 | &:hover { 25 | background-color: var(--atom-hl); 26 | } 27 | `); 28 | 29 | interface Props { 30 | id: string; 31 | isChecked: boolean; 32 | setChecked: (isChecked: boolean) => void; 33 | text: string; 34 | icon?: string; 35 | } 36 | 37 | export const IconButton = ({ id, isChecked, setChecked, text, icon }: Props) => { 38 | const toggle = () => { 39 | setChecked(!isChecked); 40 | }; 41 | 42 | return ( 43 |
44 | 48 |
49 | ); 50 | }; 51 | -------------------------------------------------------------------------------- /src/common/Components/IconPacks.tsx: -------------------------------------------------------------------------------- 1 | import styled from '@emotion/styled'; 2 | import { IconPack, iconPacks } from '~associations/IconPack'; 3 | import { IconButton } from '~common/Components/IconButton'; 4 | 5 | const Section = styled.section` 6 | display: grid; 7 | grid-template-columns: 1fr 1fr 1fr; 8 | gap: 1em; 9 | `; 10 | 11 | export const IconPacks = ({ config, setConfig }) => { 12 | const handleChange = (value: boolean, id: IconPack) => { 13 | const newConfig = { 14 | ...config, 15 | [id]: value, 16 | }; 17 | setConfig(newConfig); 18 | }; 19 | 20 | // @ts-ignore 21 | const assetsFolderUrl = chrome.runtime.getURL('assets/packs/'); 22 | 23 | return ( 24 |
25 | {iconPacks.map(({ icon, name, id }) => { 26 | return ( 27 | handleChange(e, id)} 34 | /> 35 | ); 36 | })} 37 |
38 | ); 39 | }; 40 | -------------------------------------------------------------------------------- /src/common/Components/Range.tsx: -------------------------------------------------------------------------------- 1 | import styled from '@emotion/styled'; 2 | import type { FC } from 'react'; 3 | 4 | const Section = styled.section` 5 | display: block; 6 | margin: 1rem auto; 7 | 8 | label { 9 | display: block; 10 | text-align: left; 11 | margin-bottom: 0.5em; 12 | } 13 | 14 | input { 15 | display: block; 16 | background-image: none; 17 | font-size: 0.875rem; 18 | } 19 | 20 | input[type='range'], 21 | input[type='range']::-webkit-slider-thumb { 22 | -webkit-appearance: none; 23 | background: transparent; 24 | outline: none; 25 | width: 100%; 26 | height: 2.3125rem; 27 | padding: 0.5rem; 28 | margin: 0; 29 | 30 | &:focus { 31 | outline: none; 32 | box-shadow: none; 33 | } 34 | } 35 | 36 | input[type='range'] { 37 | border: 0 none; 38 | line-height: normal; 39 | font: inherit; 40 | transition: all 0.15s ease; 41 | 42 | &:focus { 43 | &::-webkit-slider-thumb { 44 | box-shadow: 0 0 0 0.5rem rgba(0, 0, 0, 0.075); 45 | } 46 | } 47 | } 48 | 49 | input[type='range']::-webkit-slider-thumb { 50 | width: 1rem; 51 | height: 1rem; 52 | border: 0.1875rem solid var(--atom-accent); 53 | background-color: var(--atom-accent); 54 | box-shadow: 0 0 0 0.1875rem var(--atom-border); 55 | border-radius: 100%; 56 | margin-top: -0.5rem; 57 | transition: inherit; 58 | } 59 | 60 | input[type='range']::-webkit-slider-runnable-track { 61 | width: 100%; 62 | height: 0.125rem; 63 | cursor: pointer; 64 | background: var(--atom-hl); 65 | border-radius: 0.25rem; 66 | } 67 | `; 68 | 69 | type Props = { 70 | label: string; 71 | value: number; 72 | setValue: (value: number) => void; 73 | id: string; 74 | min?: number; 75 | max?: number 76 | } 77 | 78 | export const Range: FC = ({ label, value, setValue, id, min = 10, max = 30 }) => { 79 | const handleChange = (event: React.ChangeEvent) => { 80 | setValue(parseInt(event.target.value)); 81 | }; 82 | 83 | return ( 84 |
85 | 86 | 92 |
93 | ); 94 | }; 95 | -------------------------------------------------------------------------------- /src/common/constants.ts: -------------------------------------------------------------------------------- 1 | export const MONOCHROME = 'atom:isMonochrome'; 2 | export const ICON_SIZE = 'atom:iconSize'; 3 | export const ICON_COLOR = 'atom:iconColor'; 4 | export const ICON_PACKS = 'atom:iconPacks'; 5 | export const ALERT = 'atom:alert'; 6 | export const FAB = 'atom:fab'; 7 | 8 | export const CSS_VAR_ICON_SIZE = '--atom-icon-size'; 9 | export const CSS_VAR_MONOCHROME = '--atom-monochrome'; 10 | export const CSS_VAR_ICON_COLOR = '--atom-icon-color'; 11 | -------------------------------------------------------------------------------- /src/common/selectors.ts: -------------------------------------------------------------------------------- 1 | import { useStorage } from '@plasmohq/storage/hook'; 2 | import { 3 | ALERT, 4 | CSS_VAR_ICON_COLOR, 5 | CSS_VAR_ICON_SIZE, 6 | CSS_VAR_MONOCHROME, 7 | FAB, 8 | ICON_COLOR, 9 | ICON_PACKS, 10 | ICON_SIZE, 11 | MONOCHROME, 12 | } from '~common/constants'; 13 | import { changeCssVariable } from '~associations/utils'; 14 | import { useEffect, useRef, useState } from 'react'; 15 | import { type IconPacks } from '~associations/IconPack'; 16 | import { clearCache } from '~associations/files'; 17 | 18 | export const useMonochrome = () => { 19 | const { showAlert } = useAlert(); 20 | const [isMonochrome, setIsMonochrome] = useStorage(MONOCHROME, false); 21 | const [localMonochrome, setLocalMonochrome] = useState(isMonochrome); 22 | let timeoutRef = useRef(null); 23 | 24 | const handleChange = (v: boolean) => { 25 | setLocalMonochrome(v); 26 | changeCssVariable(CSS_VAR_MONOCHROME, v ? 'grayscale(1)' : 'none'); 27 | 28 | if (timeoutRef.current) { 29 | clearTimeout(timeoutRef.current); 30 | } 31 | 32 | timeoutRef.current = setTimeout(() => { 33 | setIsMonochrome(v); 34 | showAlert(); 35 | }, 100); 36 | }; 37 | 38 | useEffect(() => { 39 | setLocalMonochrome(isMonochrome); 40 | }, [isMonochrome]); 41 | 42 | return { 43 | isMonochrome, 44 | localMonochrome, 45 | setIsMonochrome: handleChange, 46 | }; 47 | }; 48 | 49 | export const useIconSize = () => { 50 | const { showAlert } = useAlert(); 51 | const [iconSize, setIconSize] = useStorage(ICON_SIZE, 20); 52 | const [localIconSize, setLocalIconSize] = useState(iconSize); 53 | let timeoutRef = useRef(null); 54 | 55 | const handleIconChange = (v: number) => { 56 | setLocalIconSize(v); 57 | changeCssVariable(CSS_VAR_ICON_SIZE, `${v}px`); 58 | 59 | if (timeoutRef.current) { 60 | clearTimeout(timeoutRef.current); 61 | } 62 | 63 | timeoutRef.current = setTimeout(() => { 64 | setIconSize(v); 65 | showAlert(); 66 | }, 600); 67 | }; 68 | 69 | useEffect(() => { 70 | setLocalIconSize(iconSize); 71 | }, [iconSize]); 72 | 73 | return { 74 | iconSize, 75 | localIconSize, 76 | setIconSize: handleIconChange, 77 | }; 78 | }; 79 | 80 | export const useIconPacks = () => { 81 | const { showAlert } = useAlert(); 82 | let timeoutRef = useRef(null); 83 | const [iconPacks, setIconPacks] = useStorage(ICON_PACKS, 84 | { 85 | angular: false, 86 | angular2: true, 87 | nest: true, 88 | nextjs: true, 89 | ngrx: true, 90 | recoil: true, 91 | rails: true, 92 | redux: true, 93 | tests: true, 94 | }, 95 | ); 96 | 97 | const handleChange = (v: IconPacks) => { 98 | setIconPacks(v); 99 | clearCache(); 100 | 101 | if (timeoutRef.current) { 102 | clearTimeout(timeoutRef.current); 103 | } 104 | 105 | timeoutRef.current = setTimeout(() => { 106 | setIconPacks(v); 107 | showAlert(); 108 | }, 600); 109 | }; 110 | 111 | return { 112 | iconPacks, 113 | setIconPacks: handleChange, 114 | }; 115 | }; 116 | 117 | export const useIconColor = () => { 118 | const { showAlert } = useAlert(); 119 | const [accentColor, setAccentColor] = useStorage(ICON_COLOR, null); 120 | let timeoutRef = useRef(null); 121 | 122 | const handleChange = (v: string) => { 123 | setAccentColor(v); 124 | changeCssVariable(CSS_VAR_ICON_COLOR, `${v}`); 125 | 126 | if (timeoutRef.current) { 127 | clearTimeout(timeoutRef.current); 128 | } 129 | 130 | timeoutRef.current = setTimeout(() => { 131 | setAccentColor(v); 132 | showAlert(); 133 | }, 600); 134 | }; 135 | 136 | return { 137 | accentColor, 138 | setAccentColor: handleChange, 139 | }; 140 | }; 141 | 142 | export const useAlert = () => { 143 | const [isAlertVisible, setIsAlertVisible] = useStorage(ALERT, false); 144 | const timeout = useRef(); 145 | 146 | const showAlert = () => { 147 | setIsAlertVisible(true); 148 | if (timeout.current) { 149 | clearTimeout(timeout.current); 150 | } 151 | 152 | timeout.current = setTimeout(() => { 153 | setIsAlertVisible(false); 154 | }, 3000); 155 | }; 156 | 157 | return { 158 | isAlertVisible, 159 | showAlert, 160 | }; 161 | }; 162 | 163 | export const useFab = () => { 164 | const { showAlert } = useAlert(); 165 | const [showFab, setShowFab] = useStorage(FAB, true); 166 | const [localShowFab, setLocalShowFab] = useState(showFab); 167 | let timeoutRef = useRef(null); 168 | 169 | const handleChange = (v: boolean) => { 170 | setLocalShowFab(v); 171 | changeCssVariable(CSS_VAR_ICON_SIZE, `${v}px`); 172 | 173 | if (timeoutRef.current) { 174 | clearTimeout(timeoutRef.current); 175 | } 176 | 177 | timeoutRef.current = setTimeout(() => { 178 | setShowFab(v); 179 | showAlert(); 180 | }, 600); 181 | }; 182 | 183 | useEffect(() => { 184 | setLocalShowFab(showFab); 185 | }, [showFab]); 186 | 187 | return { 188 | showFab, 189 | localShowFab, 190 | setShowFab: handleChange, 191 | }; 192 | }; 193 | -------------------------------------------------------------------------------- /src/common/storage.ts: -------------------------------------------------------------------------------- 1 | import { Storage } from '@plasmohq/storage'; 2 | import { ICON_COLOR, ICON_PACKS, ICON_SIZE, MONOCHROME } from '~common/constants'; 3 | import type { IconPacks } from '~associations/IconPack'; 4 | 5 | const storage = new Storage({ area: 'sync' }); 6 | 7 | export const localIsMonochrome = async () => { 8 | return await storage.get(MONOCHROME); 9 | }; 10 | 11 | export const localIconSize = async () => { 12 | return await storage.get(ICON_SIZE); 13 | }; 14 | 15 | export const localIconColor = async () => { 16 | return await storage.get(ICON_COLOR); 17 | }; 18 | 19 | export const localIconPaths = async () => { 20 | return storage.get(ICON_PACKS); 21 | }; 22 | -------------------------------------------------------------------------------- /src/content.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react'; 2 | 3 | import createCache from '@emotion/cache'; 4 | import { CacheProvider, Global } from '@emotion/react'; 5 | import { GlobalStyles } from '~Global.styled'; 6 | import { FabPopup } from '~content/FabPopup'; 7 | import { Fab } from '~content/Fab'; 8 | import { createProvider } from '~providers/ProviderFactory'; 9 | import { useFab, useIconColor, useIconPacks, useIconSize, useMonochrome } from '~common/selectors'; 10 | import { changeCssVariable } from '~associations/utils'; 11 | import { CSS_VAR_ICON_COLOR, CSS_VAR_ICON_SIZE, CSS_VAR_MONOCHROME } from '~common/constants'; 12 | import type { IconProvider } from '~providers/AbstractProvider'; 13 | 14 | const styleElement = document.createElement('style'); 15 | 16 | const styleCache = createCache({ 17 | key: 'plasmo-emotion-cache', 18 | prepend: true, 19 | container: styleElement, 20 | }); 21 | 22 | export const getStyle = () => styleElement; 23 | 24 | export const config = { 25 | css: [ 26 | '../public/global.css', 27 | ], 28 | matches: [ 29 | 'http://github.com/*', 30 | 'https://github.com/*', 31 | 'http://bitbucket.org/*', 32 | 'https://bitbucket.org/*', 33 | 'http://gitlab.com/*', 34 | 'https://gitlab.com/*', 35 | 'http://gitee.com/*', 36 | 'https://gitee.com/*', 37 | 'http://dev.azure.com/*', 38 | 'https://dev.azure.com/*', 39 | ], 40 | }; 41 | 42 | let oldHref = window.location.href; 43 | 44 | const App = () => { 45 | const [isOpen, setIsOpen] = useState(false); 46 | const [providers, setProviders] = useState>(new Set()); 47 | const { isMonochrome } = useMonochrome(); 48 | const { iconSize } = useIconSize(); 49 | const { accentColor } = useIconColor(); 50 | const { iconPacks } = useIconPacks(); 51 | const { showFab } = useFab(); 52 | 53 | const close = useCallback(() => { 54 | setIsOpen(false); 55 | }, []); 56 | 57 | const toggle = useCallback(() => { 58 | setIsOpen(open => !open); 59 | }, []); 60 | 61 | const apply = useCallback((target: ParentNode) => { 62 | const myProvider = createProvider(target); 63 | if (!myProvider) return; 64 | 65 | myProvider.injectIcons(); 66 | setProviders(providers => new Set([...providers, myProvider])); 67 | }, []); 68 | 69 | const observer = new MutationObserver((mutations) => { 70 | mutations.forEach((mutation) => { 71 | if (oldHref !== window.location.href) { 72 | oldHref = window.location.href; 73 | } 74 | if (mutation.type === 'childList') { 75 | const target = mutation.target as ParentNode; 76 | apply(target); 77 | } 78 | }); 79 | }); 80 | 81 | // On init, observe the body for changes 82 | const init = useCallback(() => { 83 | observer.observe(document.body, { 84 | attributes: true, 85 | childList: true, 86 | characterData: true, 87 | subtree: true, 88 | }); 89 | }, []); 90 | 91 | // useMountEffect 92 | useEffect(() => { 93 | document.addEventListener('turbo:load', init); 94 | init(); 95 | // applying on body in case the list is already present 96 | setTimeout(() => apply(document.body), 100); 97 | 98 | return () => { 99 | document.removeEventListener('turbo:load', init); 100 | observer.disconnect(); 101 | }; 102 | }, [apply, init]); 103 | 104 | // When the icon size or color changes, update the CSS variables 105 | useEffect(() => { 106 | changeCssVariable(CSS_VAR_MONOCHROME, isMonochrome ? 'grayscale(1)' : 'none'); 107 | changeCssVariable(CSS_VAR_ICON_SIZE, `${iconSize}px`); 108 | changeCssVariable(CSS_VAR_ICON_COLOR, accentColor); 109 | }, [isMonochrome, iconSize, accentColor]); 110 | 111 | // When the icon packs change, update the provider 112 | useEffect(() => { 113 | providers.forEach(provider => provider?.injectIcons()); 114 | }, [iconPacks]); 115 | 116 | // Listen for clicks outside the popup to close it 117 | useEffect(() => { 118 | // close on clicking on the document 119 | if (isOpen) { 120 | const listener = (event: MouseEvent) => { 121 | const target = event.target as HTMLElement; 122 | if (!target.closest('plasmo-csui')) { 123 | close(); 124 | } 125 | }; 126 | 127 | document.addEventListener('click', listener); 128 | return () => document.removeEventListener('click', listener); 129 | } 130 | }, [isOpen]); 131 | 132 | return ( 133 | 134 | 135 | 136 | {showFab && } 137 | 138 | {isOpen && } 139 | 140 | ); 141 | }; 142 | 143 | export default App; 144 | -------------------------------------------------------------------------------- /src/content/Fab.tsx: -------------------------------------------------------------------------------- 1 | import styled from '@emotion/styled'; 2 | import Logo from 'url:../assets/logo.svg'; 3 | 4 | const FabStyles = styled.button` 5 | position: fixed; 6 | bottom: 1rem; 7 | right: 1rem; 8 | width: 3rem; 9 | height: 3rem; 10 | border-radius: 50%; 11 | background-color: var(--atom-accent); 12 | color: white; 13 | border: none; 14 | cursor: pointer; 15 | opacity: 0.5; 16 | transition: opacity 0.2s ease-in-out; 17 | 18 | &:hover { 19 | background-color: var(--atom-accentT); 20 | opacity: 0.8; 21 | } 22 | `; 23 | 24 | export const Fab = ({ onClick }) => ( 25 | 27 | Logo 28 | 29 | ); 30 | -------------------------------------------------------------------------------- /src/content/FabPopup.tsx: -------------------------------------------------------------------------------- 1 | import { keyframes } from '@emotion/react'; 2 | import styled from '@emotion/styled'; 3 | import Panel from '~content/Panel/Panel'; 4 | 5 | const slideIn = keyframes` 6 | from { 7 | height: 0; 8 | opacity: 0; 9 | } 10 | 80% { 11 | height: 600px; 12 | } 13 | to { 14 | opacity: 1; 15 | height: 600px; 16 | } 17 | `; 18 | 19 | const PanelContainer = styled.div` 20 | position: fixed; 21 | bottom: 5rem; 22 | right: 2rem; 23 | width: 400px; 24 | height: 600px; 25 | background-color: var(--atom-bg, #263238); 26 | color: var(--atom-fg, #b0bec5); 27 | border: none; 28 | border-radius: 10px; 29 | 30 | animation: ${slideIn} 0.3s ease-in-out; 31 | `; 32 | 33 | export const FabPopup = () => ( 34 | 35 | 36 | 37 | ); 38 | -------------------------------------------------------------------------------- /src/content/Panel/Panel.tsx: -------------------------------------------------------------------------------- 1 | import styled from '@emotion/styled'; 2 | import Settings from '~content/Settings/Settings'; 3 | 4 | const StyledPanel = styled.div` 5 | padding-top: 1.5rem; 6 | display: grid; 7 | grid-template-columns: 100%; 8 | grid-template-rows: [content] 100% [footer] 32px; 9 | grid-template-areas: 10 | "content" 11 | "footer"; 12 | `; 13 | 14 | const Panel = () => ( 15 | 16 | 17 | 18 | ); 19 | 20 | export default Panel; 21 | -------------------------------------------------------------------------------- /src/content/Settings/Form.tsx: -------------------------------------------------------------------------------- 1 | import styled from '@emotion/styled'; 2 | import { useFab, useIconColor, useIconPacks, useIconSize, useMonochrome } from '~common/selectors'; 3 | import { IconPacks } from '~common/Components/IconPacks'; 4 | import { Checkbox } from '~common/Components/Checkbox'; 5 | import { ColorPicker } from '~common/Components/ColorPicker'; 6 | import { Range } from '~common/Components/Range'; 7 | 8 | const Section = styled.div` 9 | display: block; 10 | max-width: 20rem; 11 | text-align: left; 12 | `; 13 | 14 | const Form = () => { 15 | const { localMonochrome, setIsMonochrome } = useMonochrome(); 16 | const { localIconSize, setIconSize } = useIconSize(); 17 | const { localShowFab, setShowFab } = useFab(); 18 | const { accentColor, setAccentColor } = useIconColor(); 19 | const { iconPacks, setIconPacks } = useIconPacks(); 20 | 21 | return ( 22 | <> 23 |
24 | 30 |
31 | 32 |
33 | 39 |
40 | 41 |
42 | 48 |
49 | 50 |
51 | 57 |
58 | 59 |
60 | 61 | 65 |
66 | 67 | ); 68 | }; 69 | 70 | export default Form; 71 | -------------------------------------------------------------------------------- /src/content/Settings/Settings.tsx: -------------------------------------------------------------------------------- 1 | import styled from '@emotion/styled'; 2 | import Form from '~content/Settings/Form'; 3 | 4 | const Container = styled.main` 5 | position: relative; 6 | top: 50%; 7 | transform: translateY(-50%); 8 | width: 100%; 9 | margin: 0 auto; 10 | padding: 0 1rem; 11 | max-width: 20rem; 12 | min-width: 16rem; 13 | `; 14 | 15 | const Title = styled.header` 16 | text-align: center; 17 | margin-bottom: 1em; 18 | 19 | h1, h4 { 20 | font-weight: normal; 21 | font-style: normal; 22 | color: var(--atom-fg); 23 | text-rendering: optimizeLegibility; 24 | margin-top: 0.1rem; 25 | margin-bottom: 0.25rem; 26 | line-height: 1.4; 27 | } 28 | 29 | h1 { 30 | font-size: 2em; 31 | font-weight: lighter; 32 | line-height: 1; 33 | margin-top: 0.125em; 34 | margin-bottom: 0.125em; 35 | letter-spacing: 0.025em; 36 | animation: fadeInDownShort 0.5s cubic-bezier(0.55, 0, 0.1, 1) both 0.5s; 37 | pointer-events: none; 38 | } 39 | 40 | h4 { 41 | margin-top: 0; 42 | margin-bottom: 0; 43 | line-height: 1; 44 | font-size: 1.5em; 45 | } 46 | 47 | small { 48 | font-size: 60%; 49 | color: var(--atom-text); 50 | line-height: 0; 51 | font-weight: 300; 52 | letter-spacing: 0.05em; 53 | } 54 | `; 55 | 56 | const Grid = styled.div` 57 | display: flex; 58 | flex-direction: column; 59 | justify-content: flex-start; 60 | align-items: center; 61 | gap: 1em; 62 | `; 63 | 64 | const Settings = () => ( 65 | 66 | 67 | <h4>Atom Material Icons</h4> 68 | 69 | <h1><small>Settings</small></h1> 70 | 71 | 72 | 73 |
74 | 75 | 76 | ); 77 | 78 | export default Settings; 79 | -------------------------------------------------------------------------------- /src/popup.tsx: -------------------------------------------------------------------------------- 1 | import { Global } from '@emotion/react'; 2 | import styled from '@emotion/styled'; 3 | import { Suspense } from 'react'; 4 | import { GlobalStyles } from '~Global.styled'; 5 | import { Loading } from '~popup/Loading/Loading'; 6 | import Panel from '~popup/Panel/Panel'; 7 | 8 | const Container = styled.main` 9 | width: 100vw; 10 | height: 100vh; 11 | position: relative; 12 | display: flex; 13 | flex-direction: column; 14 | justify-content: flex-start; 15 | align-items: center; 16 | background: var(--atom-bg, #263238); 17 | color: var(--atom-g, #b0bec5); 18 | `; 19 | 20 | function delay(lazyComponent: Promise): Promise { 21 | return new Promise(resolve => setTimeout(resolve, 500)).then(() => lazyComponent); 22 | } 23 | 24 | const Popup = () => ( 25 | <> 26 | 27 | }> 28 | 29 | 30 | 31 | 32 | 33 | ); 34 | 35 | export default Popup; 36 | -------------------------------------------------------------------------------- /src/popup/Loading/Loading.tsx: -------------------------------------------------------------------------------- 1 | import styled from '@emotion/styled'; 2 | 3 | const StyledLoading = styled.div` 4 | display: flex; 5 | justify-content: center; 6 | align-items: flex-start; 7 | text-align: center; 8 | width: 100%; 9 | margin: 0 auto; 10 | padding: 0; 11 | max-width: 40rem; 12 | min-width: 24rem; 13 | background: var(--atom-bg, #263238); 14 | `; 15 | 16 | const LoadingText = styled.h4` 17 | font-weight: normal; 18 | font-style: normal; 19 | text-rendering: optimizeLegibility; 20 | color: var(--atom-fg, #b0bec5); 21 | margin-top: 0; 22 | margin-bottom: 0; 23 | line-height: 1; 24 | font-size: 2em; 25 | position: absolute; 26 | top: 50%; 27 | left: 50%; 28 | transform: translate(-50%, -50%); 29 | `; 30 | 31 | export const Loading = () => ( 32 | 33 | Loading... 34 | 35 | ); 36 | -------------------------------------------------------------------------------- /src/popup/Panel/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { keyframes } from '@emotion/react'; 2 | import styled from '@emotion/styled'; 3 | import { useAlert } from '~common/selectors'; 4 | 5 | const fadeInDown = keyframes` 6 | from { 7 | opacity: 0; 8 | height: 0; 9 | } 10 | 30% { 11 | opacity: 1; 12 | height: 2.5rem; 13 | } 14 | to { 15 | opacity: 0; 16 | height: 0; 17 | } 18 | `; 19 | 20 | const TopAlert = styled.div` 21 | position: fixed; 22 | top: 0; 23 | left: 0; 24 | width: 100%; 25 | min-width: 24rem; 26 | height: 2.5rem; 27 | padding: 0.625rem 0; 28 | background-color: var(--atom-accent); 29 | color: var(--atom-selFg); 30 | text-align: center; 31 | font-size: 0.875rem; 32 | transition: all 0.25s ease; 33 | animation: ${fadeInDown} 5s cubic-bezier(.55, 0, .1, 1) both 1s; 34 | animation-delay: 0.5s; 35 | `; 36 | 37 | export const Alert = () => { 38 | const { isAlertVisible } = useAlert(); 39 | if (!isAlertVisible) return null; 40 | 41 | return ( 42 | 43 | Refresh the page(s) to apply your changes! 44 | 45 | ); 46 | }; 47 | -------------------------------------------------------------------------------- /src/popup/Panel/Footer.tsx: -------------------------------------------------------------------------------- 1 | import styled from '@emotion/styled'; 2 | 3 | const StyledFooter = styled.footer` 4 | grid-area: footer; 5 | text-align: center; 6 | 7 | a { 8 | text-decoration: none; 9 | color: var(--atom-links, #80cbc4); 10 | } 11 | `; 12 | 13 | export const Footer = () => { 14 | const year = new Date().getFullYear(); 15 | return ( 16 | 17 | © 2015-{year} Atom Material Themes and Plugins 18 | 19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /src/popup/Panel/Panel.tsx: -------------------------------------------------------------------------------- 1 | import styled from '@emotion/styled'; 2 | import { Footer } from './Footer'; 3 | import Settings from '~popup/Settings/Settings'; 4 | import { Alert } from '~popup/Panel/Alert'; 5 | 6 | const StyledPanel = styled.div` 7 | padding-top: 2.5rem; 8 | display: grid; 9 | grid-template-columns: 100%; 10 | grid-template-rows: [content] 100% [footer] 32px; 11 | grid-template-areas: 12 | "content" 13 | "footer"; 14 | `; 15 | 16 | const Panel = () => ( 17 | 18 | 19 | 20 |