├── .all-contributorsrc ├── .azure-pipelines ├── build.docs.yml ├── build.yml ├── create-release.docs.yml ├── lint.yml ├── package.yml ├── publish-release.docs.yml ├── release.docs.yml ├── test.yml └── upload-docs.sh ├── .eslintrc ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE │ └── pull_request_template.md └── workflows │ └── azure-static-web-apps-agreeable-plant-079b5480f.yml ├── .gitignore ├── .prettierrc.js ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── azure-pipelines.docs.old.yml ├── azure-pipelines.yml ├── docs ├── .vuepress │ ├── config.js │ ├── dist │ │ ├── 404.html │ │ ├── about │ │ │ ├── code_of_conduct.html │ │ │ ├── contributing.html │ │ │ ├── history.html │ │ │ └── license.html │ │ ├── assets │ │ │ ├── affected-settings.jpg │ │ │ ├── css │ │ │ │ └── styles.baa9963c.css │ │ │ ├── element-adjustments.png │ │ │ ├── hero.png │ │ │ ├── img │ │ │ │ └── back-to-top.8b37f773.svg │ │ │ ├── js │ │ │ │ ├── 293.bdc27997.js │ │ │ │ ├── 491.f3d3f538.js │ │ │ │ ├── 812.5c9465d6.js │ │ │ │ ├── 812.5c9465d6.js.LICENSE.txt │ │ │ │ ├── app.71908e55.js │ │ │ │ ├── runtime~app.8fcc392a.js │ │ │ │ ├── v-05b2f426.62b5ae63.js │ │ │ │ ├── v-3706649a.4fa7f288.js │ │ │ │ ├── v-510ed0d4.fedc348e.js │ │ │ │ ├── v-5993bf33.64e26bd3.js │ │ │ │ ├── v-8daa1a0e.c4599b90.js │ │ │ │ ├── v-d585fbd8.d7742574.js │ │ │ │ ├── v-f6faca80.3fe5eae0.js │ │ │ │ └── v-fffb8e28.736cfea0.js │ │ │ ├── named-colors.gif │ │ │ ├── peacock-3-instances.gif │ │ │ ├── peacock-icon-small.png │ │ │ ├── peacock-icon.png │ │ │ ├── peacock-live-share-demo.gif │ │ │ ├── peacock-remote.gif │ │ │ ├── peacock-sketchnote.png │ │ │ ├── peacock-windows.png │ │ │ └── title-bar-coloring-settings.png │ │ ├── changelog │ │ │ └── index.html │ │ ├── guide │ │ │ └── index.html │ │ └── index.html │ ├── public │ │ └── assets │ │ │ ├── affected-settings.png │ │ │ ├── element-adjustments.png │ │ │ ├── hero.png │ │ │ ├── named-colors.gif │ │ │ ├── peacock-3-instances.gif │ │ │ ├── peacock-icon-small.png │ │ │ ├── peacock-icon.png │ │ │ ├── peacock-live-share-demo.gif │ │ │ ├── peacock-remote.gif │ │ │ ├── peacock-sketchnote.png │ │ │ ├── peacock-windows.png │ │ │ └── title-bar-coloring-settings.png │ └── styles │ │ ├── index.scss │ │ └── palette.scss ├── README.md ├── about │ ├── code_of_conduct.md │ ├── contributing.md │ ├── history.md │ └── license.md ├── changelog │ └── README.md └── guide │ └── README.md ├── package-lock.json ├── package.json ├── resources ├── hero.png ├── peacock-icon-small.png └── peacock-icon.png ├── src ├── apply-color.ts ├── color-library.ts ├── commands.ts ├── configuration │ ├── index.ts │ ├── read-configuration.ts │ └── update-configuration.ts ├── extension.ts ├── inputs.ts ├── live-share │ ├── enums.ts │ ├── index.ts │ ├── integration.ts │ ├── liveshare-commands.ts │ └── test │ │ ├── runTest.ts │ │ └── suite │ │ ├── index.ts │ │ └── live-share.test.ts ├── logging.ts ├── mementos.ts ├── models │ ├── constants.ts │ ├── enums.ts │ ├── favorites.ts │ ├── index.ts │ ├── interfaces.ts │ └── state.ts ├── notification.ts ├── object-library.ts ├── remote │ ├── enums.ts │ ├── index.ts │ └── integration.ts ├── statusbar.ts └── test │ ├── coverage.ts │ ├── runTest.ts │ └── suite │ ├── affected-elements.test.ts │ ├── basic.test.ts │ ├── built-in-colors.test.ts │ ├── color-input.test.ts │ ├── config-changes.test.ts │ ├── current-color.test.ts │ ├── darken-lighten.test.ts │ ├── documentation.test.ts │ ├── editing-color-settings.test.ts │ ├── element-adjustments.test.ts │ ├── favorite-colors.test.ts │ ├── foreground.test.ts │ ├── index.ts │ ├── lib │ ├── constants.ts │ └── setup-teardown-test-suite.ts │ ├── notification.test.ts │ ├── remote.test.ts │ ├── reset.test.ts │ ├── save-favorite-color.test.ts │ ├── save-starter-favorite-colors.test.ts │ ├── status-bar.test.ts │ └── surprise-me-on-startup.test.ts ├── testworkspace └── .vscode │ └── settings.json ├── tsconfig.json ├── vsc-extension-quickstart.md └── webpack.config.js /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "musicfuel", 10 | "name": "James Newell", 11 | "avatar_url": "https://avatars1.githubusercontent.com/u/1085791?v=4", 12 | "profile": "https://github.com/musicfuel", 13 | "contributions": [ 14 | "test" 15 | ] 16 | }, 17 | { 18 | "login": "JulianG", 19 | "name": "Julian", 20 | "avatar_url": "https://avatars1.githubusercontent.com/u/237818?v=4", 21 | "profile": "https://juliangaramendy.dev", 22 | "contributions": [ 23 | "ideas" 24 | ] 25 | }, 26 | { 27 | "login": "legomushroom", 28 | "name": "Oleg Solomka", 29 | "avatar_url": "https://avatars2.githubusercontent.com/u/1478800?v=4", 30 | "profile": "https://twitter.com/legomushroom", 31 | "contributions": [ 32 | "code", 33 | "test" 34 | ] 35 | }, 36 | { 37 | "login": "josephrexme", 38 | "name": "Joseph Rex", 39 | "avatar_url": "https://avatars3.githubusercontent.com/u/5395567?v=4", 40 | "profile": "https://josephrex.me", 41 | "contributions": [ 42 | "design" 43 | ] 44 | }, 45 | { 46 | "login": "samjulien", 47 | "name": "Sam Julien", 48 | "avatar_url": "https://avatars1.githubusercontent.com/u/7738189?v=4", 49 | "profile": "http://www.samjulien.com", 50 | "contributions": [ 51 | "ideas" 52 | ] 53 | }, 54 | { 55 | "login": "spboyer", 56 | "name": "Shayne Boyer", 57 | "avatar_url": "https://avatars1.githubusercontent.com/u/7681382?v=4", 58 | "profile": "http://www.tattoocoder.com", 59 | "contributions": [ 60 | "code" 61 | ] 62 | }, 63 | { 64 | "login": "burkeholland", 65 | "name": "Burke Holland", 66 | "avatar_url": "https://avatars1.githubusercontent.com/u/686963?v=4", 67 | "profile": "http://a.shinynew.me", 68 | "contributions": [ 69 | "ideas" 70 | ] 71 | }, 72 | { 73 | "login": "lostintangent", 74 | "name": "Jonathan Carter", 75 | "avatar_url": "https://avatars3.githubusercontent.com/u/116461?v=4", 76 | "profile": "http://www.lostintangent.com", 77 | "contributions": [ 78 | "code" 79 | ] 80 | }, 81 | { 82 | "login": "souzara", 83 | "name": "Ricardo Souza", 84 | "avatar_url": "https://avatars2.githubusercontent.com/u/11986361?v=4", 85 | "profile": "https://github.com/souzara", 86 | "contributions": [ 87 | "code" 88 | ] 89 | }, 90 | { 91 | "login": "kushalpandya", 92 | "name": "Kushal Pandya", 93 | "avatar_url": "https://avatars1.githubusercontent.com/u/1748044?v=4", 94 | "profile": "https://doublslash.com", 95 | "contributions": [ 96 | "code" 97 | ] 98 | }, 99 | { 100 | "login": "egamma", 101 | "name": "Erich Gamma", 102 | "avatar_url": "https://avatars1.githubusercontent.com/u/172399?v=4", 103 | "profile": "https://github.com/egamma", 104 | "contributions": [ 105 | "test" 106 | ] 107 | }, 108 | { 109 | "login": "christiannwamba", 110 | "name": "Christian Nwamba", 111 | "avatar_url": "https://avatars2.githubusercontent.com/u/8108337?v=4", 112 | "profile": "https://github.com/christiannwamba", 113 | "contributions": [ 114 | "ideas" 115 | ] 116 | }, 117 | { 118 | "login": "mjbvz", 119 | "name": "Matt Bierner", 120 | "avatar_url": "https://avatars2.githubusercontent.com/u/12821956?v=4", 121 | "profile": "http://mattbierner.com", 122 | "contributions": [ 123 | "code" 124 | ] 125 | }, 126 | { 127 | "login": "cfjedimaster", 128 | "name": "Raymond Camden", 129 | "avatar_url": "https://avatars3.githubusercontent.com/u/393660?v=4", 130 | "profile": "https://www.raymondcamden.com", 131 | "contributions": [ 132 | "ideas" 133 | ] 134 | }, 135 | { 136 | "login": "aaronpowell", 137 | "name": "Aaron Powell", 138 | "avatar_url": "https://avatars0.githubusercontent.com/u/434140?v=4", 139 | "profile": "http://www.aaron-powell.com", 140 | "contributions": [ 141 | "ideas" 142 | ] 143 | }, 144 | { 145 | "login": "tanhakabir", 146 | "name": "tanhakabir", 147 | "avatar_url": "https://avatars.githubusercontent.com/u/12758612?v=4", 148 | "profile": "https://github.com/tanhakabir", 149 | "contributions": [ 150 | "code" 151 | ] 152 | }, 153 | { 154 | "login": "ndrake", 155 | "name": "Nate Drake", 156 | "avatar_url": "https://avatars.githubusercontent.com/u/73789?v=4", 157 | "profile": "https://github.com/ndrake", 158 | "contributions": [ 159 | "code" 160 | ] 161 | } 162 | ], 163 | "contributorsPerLine": 7, 164 | "projectName": "vscode-peacock", 165 | "projectOwner": "johnpapa", 166 | "repoType": "github", 167 | "repoHost": "https://github.com", 168 | "skipCi": true 169 | } 170 | -------------------------------------------------------------------------------- /.azure-pipelines/build.docs.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: NodeTool@0 3 | displayName: 'Use $(node_version)' 4 | inputs: 5 | versionSpec: $(node_version) 6 | 7 | - task: Npm@1 8 | displayName: 'Install dependencies' 9 | inputs: 10 | verbose: false 11 | command: install 12 | 13 | - task: Npm@1 14 | displayName: 'Compile Extension' 15 | inputs: 16 | command: custom 17 | verbose: false 18 | customCommand: 'run docs:build' 19 | -------------------------------------------------------------------------------- /.azure-pipelines/build.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: NodeTool@0 3 | displayName: 'Use $(node_version)' 4 | inputs: 5 | versionSpec: $(node_version) 6 | 7 | - task: Npm@1 8 | displayName: 'Install dependencies' 9 | inputs: 10 | verbose: false 11 | command: install 12 | 13 | - task: Npm@1 14 | displayName: 'Compile Extension' 15 | inputs: 16 | command: custom 17 | verbose: false 18 | customCommand: 'run test-compile' 19 | -------------------------------------------------------------------------------- /.azure-pipelines/create-release.docs.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | # Archive files 3 | # Compress files into .7z, .tar.gz, or .zip 4 | - task: ArchiveFiles@2 5 | displayName: 'Create Docs Pipeline Artifact' 6 | inputs: 7 | rootFolderOrFile: '$(Build.SourcesDirectory)/docs/.vuepress/dist' 8 | includeRootFolder: false 9 | archiveType: 'zip' # Options: zip, 7z, tar, wim 10 | #tarCompression: 'gz' # Optional. Options: gz, bz2, xz, none 11 | archiveFile: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip' 12 | replaceExistingArchive: true 13 | #verbose: # Optional 14 | #quiet: # Optional 15 | condition: succeeded() 16 | 17 | # - task: PublishPipelineArtifact@0 18 | # displayName: 'Publish Docs Pipeline Artifact' 19 | # inputs: 20 | # targetPath: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip' 21 | # condition: succeeded() 22 | 23 | # Publish build artifacts 24 | # Publish build artifacts to Azure Pipelines or a Windows file share 25 | - task: PublishBuildArtifacts@1 26 | inputs: 27 | pathtoPublish: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip' 28 | artifactName: 'MyDocsOutput' 29 | publishLocation: 'Container' # Options: container, filePath 30 | #targetPath: # Required when publishLocation == FilePath 31 | #parallel: false # Optional 32 | #parallelCount: # Optional 33 | condition: succeeded() 34 | -------------------------------------------------------------------------------- /.azure-pipelines/lint.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: Npm@1 3 | displayName: 'Lint' 4 | inputs: 5 | command: custom 6 | customCommand: run lint 7 | -------------------------------------------------------------------------------- /.azure-pipelines/package.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - script: | 3 | npx vsce package -o release.vsix 4 | displayName: 'create vsix' 5 | condition: and(succeeded(), eq(variables['node_version'], '12.x'), eq(variables['Agent.OS'], 'Linux')) 6 | 7 | - task: PublishPipelineArtifact@0 8 | displayName: 'Publish Pipeline Artifact' 9 | inputs: 10 | targetPath: release.vsix 11 | # Only create the release for Linux on Node 12 12 | condition: and(succeeded(), eq(variables['node_version'], '12.x'), eq(variables['Agent.OS'], 'Linux')) 13 | # Instead of running this task and npm install and build again, 14 | # let's just create the vsix in a previous task to save time. 15 | # If one of them fails, the entire build fails anyway 16 | # - job: Finalize 17 | # dependsOn: 18 | # - macOS 19 | # - Linux 20 | # - Windows 21 | # condition: and(succeeded('macOS'), succeeded('Linux'), succeeded('Windows')) 22 | # pool: 23 | # # https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops&tabs=yaml#use-a-microsoft-hosted-agent 24 | # name: Hosted macOS 25 | # demands: npm 26 | # steps: 27 | # - task: NodeTool@0 28 | # displayName: 'Use Node 8.x' 29 | # inputs: 30 | # versionSpec: 8.x 31 | 32 | # - task: Npm@1 33 | # displayName: 'Install dependencies' 34 | # inputs: 35 | # verbose: false 36 | # command: install 37 | # - task: Npm@1 38 | # displayName: 'Compile TypeScript' 39 | # inputs: 40 | # command: custom 41 | # verbose: false 42 | # customCommand: 'run compile' 43 | # enabled: true 44 | # - script: | 45 | # npx vsce package -o release.vsix 46 | # displayName: 'create vsix' 47 | 48 | # - task: PublishPipelineArtifact@0 49 | # displayName: 'Publish Pipeline Artifact' 50 | # inputs: 51 | # targetPath: release.vsix 52 | -------------------------------------------------------------------------------- /.azure-pipelines/publish-release.docs.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: DownloadBuildArtifacts@0 3 | inputs: 4 | buildType: 'current' 5 | artifactName: 'MyDocsOutput' 6 | downloadType: 'single' 7 | downloadPath: '$(System.ArtifactsDirectory)' 8 | 9 | - task: ExtractFiles@1 10 | displayName: 'Extract what we just zipped - because' 11 | inputs: 12 | archiveFilePatterns: '$(System.ArtifactsDirectory)/*.zip' 13 | destinationFolder: '$(Build.ArtifactStagingDirectory)/extracted' 14 | cleanDestinationFolder: true 15 | condition: succeeded() 16 | 17 | - task: AzureCLI@1 18 | displayName: 'Upload the files to Azure Storage' 19 | inputs: 20 | azureSubscription: 'john.papa(cfd12dd7-4872-4b83-8cd8-2ed6083047d7)' 21 | scriptLocation: 'scriptPath' 22 | scriptPath: '.azure-pipelines/upload-docs.sh' 23 | arguments: '$(Build.ArtifactStagingDirectory)/extracted' 24 | condition: succeeded() 25 | -------------------------------------------------------------------------------- /.azure-pipelines/release.docs.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: AzureCLI@1 3 | displayName: 'Upload the files to Azure Storage' 4 | inputs: 5 | azureSubscription: 'john.papa(cfd12dd7-4872-4b83-8cd8-2ed6083047d7)' 6 | scriptLocation: 'scriptPath' 7 | scriptPath: '.azure-pipelines/upload-docs.sh' 8 | arguments: '$(Build.SourcesDirectory)/docs/.vuepress/dist' 9 | condition: and(succeeded(), eq(variables['node_version'], '12.x'), eq(variables['Agent.OS'], 'Linux')) 10 | -------------------------------------------------------------------------------- /.azure-pipelines/test.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | # starts a process that allows the vscode test environment to run 3 | - script: | 4 | set -e 5 | /usr/bin/Xvfb :10 -ac >> /tmp/Xvfb.out 2>&1 & 6 | disown -ar 7 | displayName: 'Start xvfb' 8 | condition: eq(variables['Agent.OS'], 'Linux') 9 | 10 | - task: Npm@1 11 | displayName: 'Run Tests' 12 | env: 13 | DISPLAY: :10 14 | inputs: 15 | command: custom 16 | verbose: false 17 | customCommand: 'run test:coverage' 18 | 19 | - task: PublishTestResults@2 20 | displayName: 'Publish Test Results' 21 | inputs: 22 | testResultsFiles: 'out-cov/*-results.xml' 23 | testRunTitle: '$(Agent.OS)-$(node_version)' 24 | condition: succeededOrFailed() 25 | 26 | - task: PublishCodeCoverageResults@1 27 | inputs: 28 | codeCoverageTool: 'cobertura' 29 | summaryFileLocation: coverage/cobertura-coverage.xml 30 | reportDirectory: coverage/lcov-report 31 | condition: succeededOrFailed() 32 | 33 | # Uncomment to push results to CodeCov.io, will need a token in a secret variable 34 | # - bash: | 35 | # bash <(curl https://codecov.io/bash) -t $TOKEN -f coverage/cobertura-coverage.xml 36 | # displayName: 'codecov' 37 | # condition: succeededOrFailed() 38 | # env: 39 | # TOKEN: $(CODECOV_TOKEN) -------------------------------------------------------------------------------- /.azure-pipelines/upload-docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | SOURCE=$1 3 | 4 | az storage blob upload-batch \ 5 | -d '$web' \ 6 | --account-name papapeacockstorage \ 7 | -s $SOURCE \ 8 | --pattern '*' 9 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "plugins": ["@typescript-eslint"], 4 | "extends": [ 5 | "plugin:@typescript-eslint/recommended", 6 | // "prettier", 7 | "prettier/@typescript-eslint", 8 | // "plugin:prettier/recommended" Enables eslint-plugin-prettier 9 | // and displays prettier errors as ESLint errors. 10 | // Make sure this is always the last configuration in the extends array. 11 | // "plugin:prettier/recommended" 12 | ], 13 | "parserOptions": { 14 | "project": "./tsconfig.json" 15 | }, 16 | "rules": { 17 | "@typescript-eslint/explicit-function-return-type": "off", 18 | "@typescript-eslint/no-parameter-properties": "off", 19 | "@typescript-eslint/no-angle-bracket-type-assertion": "off", 20 | "@typescript-eslint/no-object-literal-type-assertion": "off", 21 | "@typescript-eslint/no-use-before-define": "off", 22 | "@typescript-eslint/no-explicit-any": "off", 23 | "@typescript-eslint/no-non-null-assertion": "off", 24 | "@typescript-eslint/interface-name-prefix": "off", 25 | "@typescript-eslint/camelcase": "off", 26 | "@typescript-eslint/explicit-member-accessibility": "off" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [johnpapa] 2 | # patreon: patreon 3 | # custom: ["https://www.paypal.me/johnpapa1"] 4 | -------------------------------------------------------------------------------- /.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 | **Versions (please complete the following information):** 10 | 11 | - OS: [e.g. mac] 12 | - VS Code version [e.g. 1.37.0-insiders] 13 | - Peacock Version [e.g. 2.5.2] 14 | 15 | **Describe the bug** 16 | A clear and concise description of what the bug is. 17 | 18 | **To Reproduce** 19 | Steps to reproduce the behavior: 20 | 21 | 1. Go to '...' 22 | 2. Click on '....' 23 | 3. See error 24 | 25 | **Expected behavior** 26 | A clear and concise description of what you expected to happen. 27 | 28 | **Screenshots** 29 | If applicable, add screenshots to help explain your problem. 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.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/PULL_REQUEST_TEMPLATE/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Title 2 | 3 | ## Purpose 4 | 5 | - ... 6 | 7 | ## What 8 | 9 | - ... 10 | 11 | ## How to Test 12 | 13 | - `npm test` 14 | 15 | ## Checklist of changes included 16 | 17 | - [ ] CHANGELOG updated 18 | - [ ] README updated 19 | - [ ] Tests updated/added 20 | - [ ] Prettier formatting 21 | - [ ] console.log removed 22 | - [ ] Dead code removed 23 | - [ ] Unnecessary comments removed 24 | - [ ] Images updated (if applicable) 25 | - [ ] Version updated everywhere 26 | -------------------------------------------------------------------------------- /.github/workflows/azure-static-web-apps-agreeable-plant-079b5480f.yml: -------------------------------------------------------------------------------- 1 | name: Azure Static Web Apps CI/CD 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | types: [opened, synchronize, reopened, closed] 9 | branches: 10 | - main 11 | 12 | jobs: 13 | build_and_deploy_job: 14 | if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') 15 | runs-on: ubuntu-latest 16 | name: Build and Deploy Job 17 | steps: 18 | - uses: actions/checkout@v2 19 | with: 20 | submodules: true 21 | - name: Build And Deploy 22 | id: builddeploy 23 | uses: Azure/static-web-apps-deploy@v1 24 | with: 25 | azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_AGREEABLE_PLANT_079B5480F }} 26 | repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments) 27 | action: "upload" 28 | ###### Repository/Build Configurations - These values can be configured to match your app requirements. ###### 29 | # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig 30 | app_location: "/" # App source code path 31 | # api_location: "api" # Api source code path - optional 32 | output_location: "docs/.vuepress/dist" # Built app content directory - optional 33 | app_build_command: 'npm run docs:build' 34 | ###### End of Repository/Build Configurations ###### 35 | 36 | close_pull_request_job: 37 | if: github.event_name == 'pull_request' && github.event.action == 'closed' 38 | runs-on: ubuntu-latest 39 | name: Close Pull Request Job 40 | steps: 41 | - name: Close Pull Request 42 | id: closepullrequest 43 | uses: Azure/static-web-apps-deploy@v1 44 | with: 45 | azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_AGREEABLE_PLANT_079B5480F }} 46 | action: "close" 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | .vscode-test/ 4 | *.vsix 5 | dist 6 | !docs/.vuepress/dist 7 | out-cov 8 | coverage 9 | xunit.xml 10 | .vscode-test-web 11 | docs/.vuepress/.* 12 | .DS_Store 13 | .env -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | trailingComma: 'all', 4 | singleQuote: true, 5 | printWidth: 100, 6 | tabWidth: 2, 7 | arrowParens: 'avoid' 8 | }; 9 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | ] 6 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": ["--extensionDevelopmentPath=${workspaceFolder}"], 14 | "outFiles": ["${workspaceFolder}/dist/**/*.js"], 15 | "preLaunchTask": "npm: webpack" 16 | }, 17 | { 18 | "name": "Extension Tests", 19 | "type": "extensionHost", 20 | "request": "launch", 21 | "runtimeExecutable": "${execPath}", 22 | "args": [ 23 | "${workspaceFolder}/testworkspace", 24 | "--disable-extensions", 25 | "--extensionDevelopmentPath=${workspaceFolder}", 26 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" 27 | ], 28 | "outFiles": ["${workspaceFolder}/out/test/**/*.js"], 29 | "preLaunchTask": "npm: test-compile" 30 | }, 31 | { 32 | "name": "Extension Tests - Live Share Integration", 33 | "type": "extensionHost", 34 | "request": "launch", 35 | "runtimeExecutable": "${execPath}", 36 | "args": [ 37 | "${workspaceFolder}/testworkspace", 38 | "--extensionDevelopmentPath=${workspaceFolder}", 39 | "--extensionTestsPath=${workspaceFolder}/out/live-share/test/suite/index" 40 | ], 41 | "outFiles": ["${workspaceFolder}/out/test/**/*.js"], 42 | "preLaunchTask": "npm: test-compile" 43 | }, 44 | { 45 | "name": "Run Web Extension in VS Code", 46 | "type": "pwa-extensionHost", 47 | "debugWebWorkerHost": true, 48 | "request": "launch", 49 | "args": [ 50 | "--extensionDevelopmentPath=${workspaceFolder}", 51 | "--extensionDevelopmentKind=web" 52 | ], 53 | "outFiles": [ 54 | "${workspaceFolder}/dist/**/*.js" 55 | ], 56 | "preLaunchTask": "npm: watch" 57 | }, 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off", 11 | "workbench.statusBar.visible": true, 12 | "peacock.color": "1857a4", 13 | "workbench.colorCustomizations": { 14 | "sash.hoverBorder": "#1857a4", 15 | "activityBar.activeBackground": "#1857a4", 16 | "activityBar.background": "#1857a4", 17 | "activityBar.foreground": "#e7e7e7", 18 | "activityBar.inactiveForeground": "#e7e7e799", 19 | "activityBarBadge.background": "#530c2c", 20 | "activityBarBadge.foreground": "#e7e7e7", 21 | "titleBar.activeBackground": "#1857a4", 22 | "titleBar.activeForeground": "#e7e7e7", 23 | "titleBar.inactiveBackground": "#1857a499", 24 | "titleBar.inactiveForeground": "#e7e7e799", 25 | "statusBar.background": "#1857a4", 26 | "statusBar.foreground": "#e7e7e7", 27 | "statusBarItem.hoverBackground": "#1f6fd0", 28 | "statusBarItem.remoteBackground": "#1857a4", 29 | "statusBarItem.remoteForeground": "#e7e7e7", 30 | "activityBar.activeBorder": "#530c2c", 31 | "commandCenter.border": "#e7e7e799" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | }, 19 | { 20 | "type": "npm", 21 | "script": "watch", 22 | "group": "build", 23 | "isBackground": true, 24 | "problemMatcher": [ 25 | "$ts-webpack-watch", 26 | "$tslint-webpack-watch" 27 | ] 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | src/** 5 | .gitignore 6 | vsc-extension-quickstart.md 7 | **/tsconfig.json 8 | **/tslint.json 9 | **/*.map 10 | **/*.ts 11 | 12 | # Extra file to ignore 13 | azure-pipelines.yml 14 | .azure-pipelines/ 15 | ISSUE_TEMPLATE.md 16 | PULL_REQUEST_TEMPLATE.md 17 | .github/ 18 | vsc-extension-quickstart.md 19 | node_modules/**/test/** 20 | .all-contributorsrc 21 | node_modules 22 | out/ 23 | src/ 24 | tsconfig.json 25 | webpack.config.json 26 | coverage/ 27 | out-cov/ 28 | .eslintrc 29 | .prettierrc 30 | docs 31 | testworkspace 32 | webpack.config.js 33 | .prettierrc.js -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | The changelog can be found in the extensive [documentation here](https://www.peacockcode.dev) which includes a guide on how to use Peacock and a [changelog](https://www.peacockcode.dev/changelog/) 4 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Code of Conduct 3 | # We can even add meta tags to the page! This sets the keywords meta tag. 4 | # 5 | description: Code of Conduct guide for the Visual Studio Code Peacock extension 6 | meta: 7 | - name: keywords 8 | - content: vscode "visual studio code" peacock theme extension Code of Conduct 9 | --- 10 | # Contributor Covenant Code of Conduct 11 | 12 | ## Our Pledge 13 | 14 | In the interest of fostering an open and welcoming environment, we as 15 | contributors and maintainers pledge to making participation in our project and 16 | our community a harassment-free experience for everyone, regardless of age, body 17 | size, disability, ethnicity, sex characteristics, gender identity and expression, 18 | level of experience, education, socio-economic status, nationality, personal 19 | appearance, race, religion, or sexual identity and orientation. 20 | 21 | ## Our Standards 22 | 23 | Examples of behavior that contributes to creating a positive environment 24 | include: 25 | 26 | * Using welcoming and inclusive language 27 | * Being respectful of differing viewpoints and experiences 28 | * Gracefully accepting constructive criticism 29 | * Focusing on what is best for the community 30 | * Showing empathy towards other community members 31 | 32 | Examples of unacceptable behavior by participants include: 33 | 34 | * The use of sexualized language or imagery and unwelcome sexual attention or 35 | advances 36 | * Trolling, insulting/derogatory comments, and personal or political attacks 37 | * Public or private harassment 38 | * Publishing others' private information, such as a physical or electronic 39 | address, without explicit permission 40 | * Other conduct which could reasonably be considered inappropriate in a 41 | professional setting 42 | 43 | ## Our Responsibilities 44 | 45 | Project maintainers are responsible for clarifying the standards of acceptable 46 | behavior and are expected to take appropriate and fair corrective action in 47 | response to any instances of unacceptable behavior. 48 | 49 | Project maintainers have the right and responsibility to remove, edit, or 50 | reject comments, commits, code, wiki edits, issues, and other contributions 51 | that are not aligned to this Code of Conduct, or to ban temporarily or 52 | permanently any contributor for other behaviors that they deem inappropriate, 53 | threatening, offensive, or harmful. 54 | 55 | ## Scope 56 | 57 | This Code of Conduct applies both within project spaces and in public spaces 58 | when an individual is representing the project or its community. Examples of 59 | representing a project or community include using an official project e-mail 60 | address, posting via an official social media account, or acting as an appointed 61 | representative at an online or offline event. Representation of a project may be 62 | further defined and clarified by project maintainers. 63 | 64 | ## Enforcement 65 | 66 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 67 | reported by contacting the project team at john+github@johnpapa.net. All 68 | complaints will be reviewed and investigated and will result in a response that 69 | is deemed necessary and appropriate to the circumstances. The project team is 70 | obligated to maintain confidentiality with regard to the reporter of an incident. 71 | Further details of specific enforcement policies may be posted separately. 72 | 73 | Project maintainers who do not follow or enforce the Code of Conduct in good 74 | faith may face temporary or permanent repercussions as determined by other 75 | members of the project's leadership. 76 | 77 | ## Attribution 78 | 79 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 80 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 81 | 82 | [homepage]: https://www.contributor-covenant.org 83 | 84 | For answers to common questions about this code of conduct, see 85 | https://www.contributor-covenant.org/faq 86 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We would love for you to contribute and help make it even better 4 | than it is today! As a contributor, here are the guidelines we would like you 5 | to follow: 6 | 7 | - [Code of Conduct](#coc) 8 | 9 | - [Issues and Bugs](#issue) 10 | - [Feature Requests](#feature) 11 | - [Submission Guidelines](#submit) 12 | 13 | ## Code of Conduct 14 | 15 | Help us keep this project open and inclusive. Please read and follow our [Code of Conduct](./CODE_OF_CONDUCT.md). 16 | 17 | ## Found an Issue? 18 | 19 | If you find a bug in the source code or a mistake in the documentation, you can help us by 20 | [submitting an issue](#submit-issue) to our [GitHub Repository](https://github.com/johnpapa/vscode-peacock). Even better, you can 21 | [submit a Pull Request](#submit-pr) with a fix. 22 | 23 | ## Want a Feature? 24 | 25 | You can _request_ a new feature by [submitting an issue](#submit-issue) to our [GitHub Repository](https://github.com/johnpapa/vscode-peacock). If you would like to _implement_ a new feature, please submit an issue with 26 | a proposal for your work first, to be sure that we can use it. 27 | 28 | - **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr). 29 | 30 | ## Submission Guidelines 31 | 32 | ### Submitting an Issue 33 | 34 | Before you submit an issue, search the archive, maybe your question was already answered. 35 | 36 | If your issue appears to be a bug, and hasn't been reported, open a new issue. 37 | Help us to maximize the effort we can spend fixing issues and adding new 38 | features, by not reporting duplicate issues. Providing the following information will increase the 39 | chances of your issue being dealt with quickly: 40 | 41 | - **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps 42 | - **Version** - what version is affected (e.g. 3.9.0) 43 | - **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you 44 | - **Browsers and Operating System** - is this a problem with all browsers? 45 | - **Reproduce the Error** - provide a live example or a unambiguous set of steps 46 | - **Related Issues** - has a similar issue been reported before? 47 | - **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be 48 | causing the problem (line of code or commit) 49 | 50 | You can file new issues by providing the above information [here](https://github.com/johnpapa/vscode-peacock/issues/new). 51 | 52 | ### Submitting a Pull Request (PR) 53 | 54 | Before you submit your Pull Request (PR) consider the following guidelines: 55 | 56 | - Search [GitHub](https://github.com/johnpapa/vscode-peacock/pulls) for an open or closed PR 57 | that relates to your submission. You don't want to duplicate effort. 58 | 59 | - Make your changes in a new git fork. 60 | - Commit your changes using a descriptive commit message. 61 | - Push your fork to GitHub. 62 | - In GitHub, submit a Pull Request. 63 | - If we suggest changes then: 64 | - Make the required updates. 65 | - Rebase your fork and force push to your GitHub repository (this will update your Pull Request): 66 | 67 | ```shell 68 | git rebase main -i 69 | git push -f 70 | ``` 71 | 72 | That's it! Thank you for your contribution! 73 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) John Papa. 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 | -------------------------------------------------------------------------------- /azure-pipelines.docs.old.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | branches: 3 | include: 4 | - main 5 | # wwe only publish docs when we push to main. We shuold have a docs staging site tho. 6 | # pr: 7 | # branches: 8 | # include: 9 | # - '*' 10 | # # must quote since "*" is a YAML reserved character; we want a string 11 | 12 | stages: 13 | - stage: Build_the_Docs 14 | jobs: 15 | - job: Linux 16 | pool: 17 | name: Hosted Ubuntu 1604 18 | demands: npm 19 | strategy: 20 | matrix: 21 | node_12_x: 22 | node_version: 12.x 23 | 24 | steps: 25 | - template: .azure-pipelines/build.docs.yml 26 | - template: .azure-pipelines/create-release.docs.yml 27 | 28 | - stage: Release_the_docs 29 | jobs: 30 | - job: Push_to_Storage 31 | pool: 32 | name: Hosted Ubuntu 1604 33 | strategy: 34 | matrix: 35 | node_12_x: 36 | node_version: 12.x 37 | 38 | steps: 39 | - template: .azure-pipelines/publish-release.docs.yml 40 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | branches: 3 | include: 4 | - main 5 | pr: 6 | branches: 7 | include: 8 | - '*' 9 | # must quote since "*" is a YAML reserved character; we want a string 10 | 11 | stages: 12 | - stage: Build_the_Extension 13 | jobs: 14 | - job: OS_and_Platform 15 | strategy: 16 | matrix: 17 | linux_node_14: 18 | imageName: Hosted Ubuntu 1604 19 | node_version: 14.x 20 | # linux_node_12: 21 | # imageName: Hosted Ubuntu 1604 22 | # node_version: 12.x 23 | mac_node_14: 24 | imageName: Hosted macOS 25 | node_version: 14.x 26 | # mac_node_12: 27 | # imageName: Hosted macOS 28 | # node_version: 12.x 29 | windows_node_14: 30 | imageName: Hosted VS2017 31 | node_version: 14.x 32 | # windows_node_12: 33 | # imageName: Hosted VS2017 34 | # node_version: 12.x 35 | pool: 36 | name: $(imageName) 37 | # pool: 38 | # https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops&tabs=yaml#use-a-microsoft-hosted-agent 39 | # name: Hosted Ubuntu 1604 40 | demands: npm 41 | 42 | steps: 43 | - template: .azure-pipelines/build.yml 44 | - template: .azure-pipelines/lint.yml 45 | - template: .azure-pipelines/test.yml 46 | - template: .azure-pipelines/package.yml 47 | -------------------------------------------------------------------------------- /docs/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | title: 'Peacock', 3 | lang: 'en-US', 4 | description: 'Coloring your world, one Code editor at a time', 5 | ga: 'your-ga-id', 6 | markdown: { 7 | code: { 8 | lineNumbers: true, 9 | }, 10 | }, 11 | plugins: [ 12 | // require('@vuepress/plugin-google-analytics'), 13 | // { 14 | // ga: 'your-ga-id', 15 | // }, 16 | ], 17 | themeConfig: { 18 | search: true, 19 | searchMaxSuggestions: 10, 20 | sidebar: 'auto', 21 | navbar: [ 22 | { text: 'Home', link: '/' }, 23 | { text: 'Guide', link: '/guide/' }, 24 | { text: 'ChangeLog', link: '/changelog/' }, 25 | { 26 | text: 'About', 27 | children: [ 28 | { text: 'About', link: '/about/history.md' }, 29 | { text: 'Code of Conduct', link: '/about/code_of_conduct.md' }, 30 | { text: 'Contributing', link: '/about/contributing.md' }, 31 | { text: 'License', link: '/about/license.md' }, 32 | ], 33 | }, 34 | { text: '@john_papa', link: 'https://twitter.com/john_papa' }, 35 | ], 36 | logo: '/assets/peacock-icon-small.png', 37 | // Assumes GitHub. Can also be a full GitLab url. 38 | repo: 'johnpapa/vscode-peacock', 39 | // Customising the header label 40 | // Defaults to "GitHub"/"GitLab"/"Bitbucket" depending on `themeConfig.repo` 41 | repoLabel: 'GitHub', 42 | 43 | // Optional options for generating "Edit this page" link 44 | 45 | // if your docs are in a different repo from your main project: 46 | docsRepo: 'johnpapa/vscode-peacock', 47 | // if your docs are not at the root of the repo: 48 | docsDir: 'docs', 49 | // if your docs are in a specific branch (defaults to 'main'): 50 | docsBranch: 'main', 51 | // defaults to false, set to true to enable 52 | editLinks: true, 53 | // custom text for edit link. Defaults to "Edit this page" 54 | editLinkText: 'Help us improve this page!' 55 | }, 56 | }; 57 | -------------------------------------------------------------------------------- /docs/.vuepress/dist/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Peacock 8 | 9 | 10 | 11 | 12 |

404

How did we get here?
Take me home
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/affected-settings.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/dist/assets/affected-settings.jpg -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/element-adjustments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/dist/assets/element-adjustments.png -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/dist/assets/hero.png -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/img/back-to-top.8b37f773.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/491.f3d3f538.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkvscode_peacock=self.webpackChunkvscode_peacock||[]).push([[491],{8491:(e,o,t)=>{t.r(o),t.d(o,{default:()=>h});var l=t(6252),u=t(3577),a=t(2262),n=t(7621),c=t(45);const s={class:"theme-container"},r={class:"theme-default-content"},d=(0,l._)("h1",null,"404",-1),h=(0,l.aZ)({setup(e){var o,t,h;const k=(0,n.I)(),v=(0,c.X6)(),m=null!=(o=v.value.notFound)?o:["Not Found"],p=null!=(t=v.value.home)?t:k.value,f=null!=(h=v.value.backToHome)?h:"Back to home";return(e,o)=>{const t=(0,l.up)("RouterLink");return(0,l.wg)(),(0,l.iD)("div",s,[(0,l._)("div",r,[d,(0,l._)("blockquote",null,(0,u.zw)(m[Math.floor(Math.random()*m.length)]),1),(0,l.Wm)(t,{to:(0,a.SU)(p)},{default:(0,l.w5)((()=>[(0,l.Uk)((0,u.zw)((0,a.SU)(f)),1)])),_:1},8,["to"])])])}}})}}]); -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/812.5c9465d6.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress 2 | * @license MIT */ 3 | 4 | /*! 5 | * vue-router v4.0.11 6 | * (c) 2021 Eduardo San Martin Morote 7 | * @license MIT 8 | */ 9 | -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/app.71908e55.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkvscode_peacock=self.webpackChunkvscode_peacock||[]).push([[143],{3131:(e,t,n)=>{n.d(t,{g:()=>i});var o=n(2009),a=n(6971),d=n(1598);const i=[o.Z,a.Z,d.Z]},9947:(e,t,n)=>{n.d(t,{p:()=>o});const o=[n(3051).Z]},4611:(e,t,n)=>{n.d(t,{l:()=>i});var o=n(8866),a=n(1263),d=n(6243);const i=[o.Z,a.Z,d.Z]},4150:(e,t,n)=>{n.d(t,{Z:()=>a});var o=n(6252);const a={404:(0,o.RC)((()=>n.e(491).then(n.bind(n,8491)))),Layout:(0,o.RC)((()=>n.e(293).then(n.bind(n,3293))))}},6056:(e,t,n)=>{n.d(t,{b:()=>a});var o=n(6252);const a={"v-8daa1a0e":(0,o.RC)((()=>n.e(509).then(n.bind(n,1829)))),"v-05b2f426":(0,o.RC)((()=>n.e(726).then(n.bind(n,3052)))),"v-d585fbd8":(0,o.RC)((()=>n.e(999).then(n.bind(n,2848)))),"v-f6faca80":(0,o.RC)((()=>n.e(343).then(n.bind(n,6435)))),"v-5993bf33":(0,o.RC)((()=>n.e(967).then(n.bind(n,7736)))),"v-510ed0d4":(0,o.RC)((()=>n.e(495).then(n.bind(n,1213)))),"v-fffb8e28":(0,o.RC)((()=>n.e(807).then(n.bind(n,9534)))),"v-3706649a":(0,o.RC)((()=>n.e(88).then(n.bind(n,8109))))}},9706:(e,t,n)=>{n.d(t,{T:()=>o});const o={"v-8daa1a0e":()=>n.e(509).then(n.bind(n,6464)).then((({data:e})=>e)),"v-05b2f426":()=>n.e(726).then(n.bind(n,6343)).then((({data:e})=>e)),"v-d585fbd8":()=>n.e(999).then(n.bind(n,9853)).then((({data:e})=>e)),"v-f6faca80":()=>n.e(343).then(n.bind(n,537)).then((({data:e})=>e)),"v-5993bf33":()=>n.e(967).then(n.bind(n,872)).then((({data:e})=>e)),"v-510ed0d4":()=>n.e(495).then(n.bind(n,3707)).then((({data:e})=>e)),"v-fffb8e28":()=>n.e(807).then(n.bind(n,9570)).then((({data:e})=>e)),"v-3706649a":()=>n.e(88).then(n.bind(n,1801)).then((({data:e})=>e))}},4634:(e,t,n)=>{n.d(t,{g:()=>a});var o=n(4802);const a=[["v-8daa1a0e","/","",["/index.html","/README.md"]],["v-05b2f426","/about/code_of_conduct.html","Code of Conduct",["/about/code_of_conduct","/about/code_of_conduct.md"]],["v-d585fbd8","/about/contributing.html","Contributing",["/about/contributing","/about/contributing.md"]],["v-f6faca80","/about/history.html","About",["/about/history","/about/history.md"]],["v-5993bf33","/about/license.html","License",["/about/license","/about/license.md"]],["v-510ed0d4","/changelog/","Changelog",["/changelog/index.html","/changelog/README.md"]],["v-fffb8e28","/guide/","Guide",["/guide/index.html","/guide/README.md"]],["v-3706649a","/404.html","",["/404"]]].reduce(((e,[t,n,a,d])=>(e.push({name:t,path:n,component:o.Y,meta:{title:a}},...d.map((e=>({path:e,redirect:n})))),e)),[{name:"404",path:"/:catchAll(.*)",component:o.Y}])},5220:(e,t,n)=>{n.d(t,{H:()=>o});const o={base:"/",lang:"en-US",title:"Peacock",description:"Coloring your world, one Code editor at a time",head:[],locales:{}}},2232:(e,t,n)=>{n.d(t,{f:()=>o});const o={search:!0,searchMaxSuggestions:10,sidebar:"auto",navbar:[{text:"Home",link:"/"},{text:"Guide",link:"/guide/"},{text:"ChangeLog",link:"/changelog/"},{text:"About",children:[{text:"About",link:"/about/history.md"},{text:"Code of Conduct",link:"/about/code_of_conduct.md"},{text:"Contributing",link:"/about/contributing.md"},{text:"License",link:"/about/license.md"}]},{text:"@john_papa",link:"https://twitter.com/john_papa"}],logo:"/assets/peacock-icon-small.png",repo:"johnpapa/vscode-peacock",repoLabel:"GitHub",docsRepo:"johnpapa/vscode-peacock",docsDir:"docs",docsBranch:"main",editLinks:!0,editLinkText:"Help us improve this page!",locales:{"/":{selectLanguageName:"English"}},darkMode:!0,selectLanguageText:"Languages",selectLanguageAriaLabel:"Select language",sidebarDepth:2,editLink:!0,lastUpdated:!0,lastUpdatedText:"Last Updated",contributors:!0,contributorsText:"Contributors",notFound:["There's nothing here.","How did we get here?","That's a Four-Oh-Four.","Looks like we've got some broken links."],backToHome:"Take me home",openInNewWindow:"open in new window",toggleDarkMode:"toggle dark mode",toggleSidebar:"toggle sidebar"}}},e=>{e.O(0,[460,812],(()=>(5698,e(e.s=5698)))),e.O()}]); -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/runtime~app.8fcc392a.js: -------------------------------------------------------------------------------- 1 | (()=>{"use strict";var e,r,t,a={},o={};function n(e){var r=o[e];if(void 0!==r)return r.exports;var t=o[e]={exports:{}};return a[e].call(t.exports,t,t.exports,n),t.exports}n.m=a,e=[],n.O=(r,t,a,o)=>{if(!t){var s=1/0;for(l=0;l=o)&&Object.keys(n.O).every((e=>n.O[e](t[d])))?t.splice(d--,1):(i=!1,o0&&e[l-1][2]>o;l--)e[l]=e[l-1];e[l]=[t,a,o]},n.d=(e,r)=>{for(var t in r)n.o(r,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce(((r,t)=>(n.f[t](e,r),r)),[])),n.u=e=>"assets/js/"+({88:"v-3706649a",343:"v-f6faca80",495:"v-510ed0d4",509:"v-8daa1a0e",726:"v-05b2f426",807:"v-fffb8e28",967:"v-5993bf33",999:"v-d585fbd8"}[e]||e)+"."+{88:"4fa7f288",293:"bdc27997",343:"3fe5eae0",491:"f3d3f538",495:"fedc348e",509:"c4599b90",726:"62b5ae63",807:"736cfea0",967:"64e26bd3",999:"d7742574"}[e]+".js",n.miniCssF=e=>"assets/css/styles.baa9963c.css",n.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r={},t="vscode-peacock:",n.l=(e,a,o,s)=>{if(r[e])r[e].push(a);else{var i,d;if(void 0!==o)for(var c=document.getElementsByTagName("script"),l=0;l{i.onerror=i.onload=null,clearTimeout(v);var o=r[e];if(delete r[e],i.parentNode&&i.parentNode.removeChild(i),o&&o.forEach((e=>e(a))),t)return t(a)},v=setTimeout(u.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=u.bind(null,i.onerror),i.onload=u.bind(null,i.onload),d&&document.head.appendChild(i)}},n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.p="/",(()=>{var e={523:0,460:0};n.f.j=(r,t)=>{var a=n.o(e,r)?e[r]:void 0;if(0!==a)if(a)t.push(a[2]);else if(/^(460|523)$/.test(r))e[r]=0;else{var o=new Promise(((t,o)=>a=e[r]=[t,o]));t.push(a[2]=o);var s=n.p+n.u(r),i=new Error;n.l(s,(t=>{if(n.o(e,r)&&(0!==(a=e[r])&&(e[r]=void 0),a)){var o=t&&("load"===t.type?"missing":t.type),s=t&&t.target&&t.target.src;i.message="Loading chunk "+r+" failed.\n("+o+": "+s+")",i.name="ChunkLoadError",i.type=o,i.request=s,a[1](i)}}),"chunk-"+r,r)}},n.O.j=r=>0===e[r];var r=(r,t)=>{var a,o,[s,i,d]=t,c=0;if(s.some((r=>0!==e[r]))){for(a in i)n.o(i,a)&&(n.m[a]=i[a]);if(d)var l=d(n)}for(r&&r(t);c{i.r(t),i.d(t,{data:()=>o});const o={key:"v-05b2f426",path:"/about/code_of_conduct.html",title:"Code of Conduct",lang:"en-US",frontmatter:{title:"Code of Conduct",description:"Code of Conduct guide for the Visual Studio Code Peacock extension",meta:[{name:"keywords"},{content:'vscode "visual studio code" peacock theme extension Code of Conduct'}]},excerpt:"",headers:[{level:2,title:"Our Pledge",slug:"our-pledge",children:[]},{level:2,title:"Our Standards",slug:"our-standards",children:[]},{level:2,title:"Our Responsibilities",slug:"our-responsibilities",children:[]},{level:2,title:"Scope",slug:"scope",children:[]},{level:2,title:"Enforcement",slug:"enforcement",children:[]},{level:2,title:"Attribution",slug:"attribution",children:[]}],filePathRelative:"about/code_of_conduct.md",git:{updatedTime:158994316e4,contributors:[{name:"John Papa",email:"john@johnpapa.net",commits:1}]}}},3052:(e,t,i)=>{i.r(t),i.d(t,{default:()=>p});var o=i(6252);const n=(0,o.uE)('

Contributor Covenant Code of Conduct

Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.

Our Standards

Examples of behavior that contributes to creating a positive environment include:

  • Using welcoming and inclusive language
  • Being respectful of differing viewpoints and experiences
  • Gracefully accepting constructive criticism
  • Focusing on what is best for the community
  • Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

  • The use of sexualized language or imagery and unwelcome sexual attention or advances
  • Trolling, insulting/derogatory comments, and personal or political attacks
  • Public or private harassment
  • Publishing others' private information, such as a physical or electronic address, without explicit permission
  • Other conduct which could reasonably be considered inappropriate in a professional setting

Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.

Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at john+github@johnpapa.net. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

Attribution

',17),a=(0,o.Uk)("This Code of Conduct is adapted from the "),r={href:"https://www.contributor-covenant.org",target:"_blank",rel:"noopener noreferrer"},s=(0,o.Uk)("Contributor Covenant"),c=(0,o.Uk)(", version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html"),d=(0,o._)("p",null,"For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq",-1),l={},p=(0,i(3744).Z)(l,[["render",function(e,t){const i=(0,o.up)("OutboundLink");return(0,o.wg)(),(0,o.iD)(o.HY,null,[n,(0,o._)("p",null,[a,(0,o._)("a",r,[s,(0,o.Wm)(i)]),c]),d],64)}]])},3744:(e,t)=>{t.Z=(e,t)=>{for(const[i,o]of t)e[i]=o;return e}}}]); -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/v-3706649a.4fa7f288.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkvscode_peacock=self.webpackChunkvscode_peacock||[]).push([[88],{1801:(e,t,a)=>{a.r(t),a.d(t,{data:()=>n});const n={key:"v-3706649a",path:"/404.html",title:"",lang:"en-US",frontmatter:{layout:"404"},excerpt:"",headers:[],filePathRelative:null,git:{}}},8109:(e,t,a)=>{a.r(t),a.d(t,{default:()=>c});const n={},c=(0,a(3744).Z)(n,[["render",function(e,t){return null}]])},3744:(e,t)=>{t.Z=(e,t)=>{for(const[a,n]of t)e[a]=n;return e}}}]); -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/v-5993bf33.64e26bd3.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkvscode_peacock=self.webpackChunkvscode_peacock||[]).push([[967],{872:(e,t,n)=>{n.r(t),n.d(t,{data:()=>o});const o={key:"v-5993bf33",path:"/about/license.html",title:"License",lang:"en-US",frontmatter:{title:"License",description:"License for the Visual Studio Code Peacock extension",meta:[{name:"keywords"},{content:'vscode "visual studio code" peacock theme extension license'}]},excerpt:"",headers:[],filePathRelative:"about/license.md",git:{updatedTime:158994316e4,contributors:[{name:"John Papa",email:"john@johnpapa.net",commits:1}]}}},7736:(e,t,n)=>{n.r(t),n.d(t,{default:()=>a});var o=n(6252);const i=[(0,o._)("code",null,'MIT License\n\nCopyright (c) JohnPapa.net, LLC. All rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the "Software"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE\n',-1)],s={},a=(0,n(3744).Z)(s,[["render",function(e,t){return(0,o.wg)(),(0,o.iD)("pre",null,i)}]])},3744:(e,t)=>{t.Z=(e,t)=>{for(const[n,o]of t)e[n]=o;return e}}}]); -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/v-8daa1a0e.c4599b90.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkvscode_peacock=self.webpackChunkvscode_peacock||[]).push([[509],{6464:(e,t,o)=>{o.r(t),o.d(t,{data:()=>a});const a={key:"v-8daa1a0e",path:"/",title:"",lang:"en-US",frontmatter:{home:!0,heroImage:"assets/hero.png",actions:[{text:"Get Started →",link:"/guide/",type:"primary"}],features:[{title:"Work More Efficiently",details:"Quickly identify each of your Visual Studio Code instances using your favorite colors"},{title:"Remote Integration",details:"Color your Visual Studio Code editor uniquely when you are using the remote integration features."},{title:"Live Share",details:"Color your Visual Studio Code editor uniquely when you are in a Live Share session as a Guest or a Host"}],footer:"MIT Licensed | Copyright © 2019-present John Papa | current version 3.10.0"},excerpt:"",headers:[],filePathRelative:"README.md",git:{updatedTime:1633217573e3,contributors:[{name:"John Papa",email:"john@johnpapa.net",commits:3},{name:"John Papa",email:"johnpapa@users.noreply.github.com",commits:2}]}}},1829:(e,t,o)=>{o.r(t),o.d(t,{default:()=>i});const a={},i=(0,o(3744).Z)(a,[["render",function(e,t){return null}]])},3744:(e,t)=>{t.Z=(e,t)=>{for(const[o,a]of t)e[o]=a;return e}}}]); -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/v-d585fbd8.d7742574.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkvscode_peacock=self.webpackChunkvscode_peacock||[]).push([[999],{9853:(e,i,t)=>{t.r(i),t.d(i,{data:()=>a});const a={key:"v-d585fbd8",path:"/about/contributing.html",title:"Contributing",lang:"en-US",frontmatter:{title:"Contributing",description:"Conributions guide for the Visual Studio Code Peacock extension",meta:[{name:"keywords"},{content:'vscode "visual studio code" peacock theme extension contributions'}]},excerpt:"",headers:[{level:2,title:"Code of Conduct",slug:"code-of-conduct",children:[]},{level:2,title:"Found an Issue?",slug:"found-an-issue",children:[]},{level:2,title:"Want a Feature?",slug:"want-a-feature",children:[]},{level:2,title:"Submission Guidelines",slug:"submission-guidelines",children:[{level:3,title:"Submitting an Issue",slug:"submitting-an-issue",children:[]},{level:3,title:"Submitting a Pull Request (PR)",slug:"submitting-a-pull-request-pr",children:[]}]}],filePathRelative:"about/contributing.md",git:{updatedTime:158994316e4,contributors:[{name:"John Papa",email:"john@johnpapa.net",commits:1}]}}},2848:(e,i,t)=>{t.r(i),t.d(i,{default:()=>M});var a=t(6252);const n=(0,a.uE)('

Contributing

We would love for you to contribute and help make it even better than it is today! As a contributor, here are the guidelines we would like you to follow:

-Code of Conduct

Code of Conduct

Help us keep this project open and inclusive.Please read and follow our Code of Conduct.

Found an Issue?

',7),s=(0,a.Uk)("If you find a bug in the source code or a mistake in the documentation, you can help us by "),u=(0,a._)("a",{href:"#submit-issue"},"submitting an issue",-1),o=(0,a.Uk)(" to our "),r={href:"https://github.com/johnpapa/vscode-peacock",target:"_blank",rel:"noopener noreferrer"},l=(0,a.Uk)("GitHub Repository"),d=(0,a.Uk)(". Even better, you can "),c=(0,a._)("a",{href:"#submit-pr"},"submit a Pull Request",-1),h=(0,a.Uk)(" with a fix."),p=(0,a._)("h2",{id:"want-a-feature",tabindex:"-1"},[(0,a._)("a",{class:"header-anchor",href:"#want-a-feature","aria-hidden":"true"},"#"),(0,a.Uk)(),(0,a._)("a",{name:"feature"}),(0,a.Uk)(" Want a Feature?")],-1),b=(0,a.Uk)("You can "),f=(0,a._)("em",null,"request",-1),g=(0,a.Uk)(" a new feature by "),m=(0,a._)("a",{href:"#submit-issue"},"submitting an issue",-1),k=(0,a.Uk)(" to our "),y={href:"https://github.com/johnpapa/vscode-peacock",target:"_blank",rel:"noopener noreferrer"},w=(0,a.Uk)("GitHub Repository"),v=(0,a.Uk)(".If you would like to "),_=(0,a._)("em",null,"implement",-1),U=(0,a.Uk)(" a new feature, please submit an issue with a proposal for your work first, to be sure that we can use it."),x=(0,a.uE)('

Submission Guidelines

Submitting an Issue

Before you submit an issue, search the archive, maybe your question was already answered.

If your issue appears to be a bug, and hasn't been reported, open a new issue. Help us to maximize the effort we can spend fixing issues and adding new features, by not reporting duplicate issues.Providing the following information will increase the chances of your issue being dealt with quickly:

  • Overview of the Issue - if an error is being thrown a non- minified stack trace helps
    • Version - what version is affected (e.g. 0.1.2)
      • Motivation for or Use Case - explain what are you trying to do and why the current behavior is a bug for you
        • Browsers and Operating System - is this a problem with all browsers?
  • Reproduce the Error - provide a live example or a unambiguous set of steps
    • Related Issues - has a similar issue been reported before?
  • Suggest a Fix - if you can't fix the bug yourself, perhaps you can point to what might be causing the problem (line of code or commit)
',6),C=(0,a.Uk)("You can file new issues by providing the above information "),R={href:"https://github.com/johnpapa/vscode-peacock/issues/new",target:"_blank",rel:"noopener noreferrer"},P=(0,a.Uk)("here"),q=(0,a.Uk)("."),S=(0,a._)("h3",{id:"submitting-a-pull-request-pr",tabindex:"-1"},[(0,a._)("a",{class:"header-anchor",href:"#submitting-a-pull-request-pr","aria-hidden":"true"},"#"),(0,a.Uk)(),(0,a._)("a",{name:"submit-pr"}),(0,a.Uk)(" Submitting a Pull Request (PR)")],-1),I=(0,a._)("p",null,"Before you submit your Pull Request (PR) consider the following guidelines:",-1),G=(0,a.Uk)("Search "),H={href:"https://github.com/johnpapa/vscode-peacock/pulls",target:"_blank",rel:"noopener noreferrer"},j=(0,a.Uk)("GitHub"),F=(0,a.Uk)(" for an open or closed PR that relates to your submission.You don't want to duplicate effort."),W=(0,a._)("ul",null,[(0,a._)("li",null,"Make your changes in a new git fork:")],-1),E=(0,a.uE)('
  • Commit your changes using a descriptive commit message

    • Push your fork to GitHub:
  • In GitHub, send a pull request

    • If we suggest changes then:

    • Make the required updates.

    • Rebase your fork and force push to your GitHub repository (this will update your Pull Request):

      git rebase master -i\ngit push -f\n
      1
      2
  • ',2),B=(0,a._)("p",null,"That's it! Thank you for your contribution!",-1),Y={},M=(0,t(3744).Z)(Y,[["render",function(e,i){const t=(0,a.up)("OutboundLink");return(0,a.wg)(),(0,a.iD)(a.HY,null,[n,(0,a._)("p",null,[s,u,o,(0,a._)("a",r,[l,(0,a.Wm)(t)]),d,c,h]),p,(0,a._)("p",null,[b,f,g,m,k,(0,a._)("a",y,[w,(0,a.Wm)(t)]),v,_,U]),x,(0,a._)("p",null,[C,(0,a._)("a",R,[P,(0,a.Wm)(t)]),q]),S,I,(0,a._)("ul",null,[(0,a._)("li",null,[(0,a._)("p",null,[G,(0,a._)("a",H,[j,(0,a.Wm)(t)]),F]),W]),E]),B],64)}]])},3744:(e,i)=>{i.Z=(e,i)=>{for(const[t,a]of i)e[t]=a;return e}}}]); -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/v-f6faca80.3fe5eae0.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkvscode_peacock=self.webpackChunkvscode_peacock||[]).push([[343],{537:(e,t,o)=>{o.r(t),o.d(t,{data:()=>a});const a={key:"v-f6faca80",path:"/about/history.html",title:"About",lang:"en-US",frontmatter:{title:"About",description:"About the Visual Studio Code and the Peacock Extension",meta:[{name:"keywords"},{content:'vscode "visual studio code" peacock theme extension'}]},excerpt:"",headers:[{level:2,title:"Why Peacock",slug:"why-peacock",children:[]},{level:2,title:"History of Peacock",slug:"history-of-peacock",children:[]}],filePathRelative:"about/history.md",git:{updatedTime:158994316e4,contributors:[{name:"John Papa",email:"john@johnpapa.net",commits:1}]}}},6435:(e,t,o)=>{o.r(t),o.d(t,{default:()=>W});var a=o(6252);const i=(0,a._)("h1",{id:"about-peacock",tabindex:"-1"},[(0,a._)("a",{class:"header-anchor",href:"#about-peacock","aria-hidden":"true"},"#"),(0,a.Uk)(" About Peacock")],-1),n=(0,a._)("p",null,"Subtly change the color of your Visual Studio (VS) Code workspace. Ideal when you have multiple VS Code instances, use Visual Studio Live Share, or use VS Code's Remote features, and you want to quickly identify your editor.",-1),c=(0,a._)("h2",{id:"why-peacock",tabindex:"-1"},[(0,a._)("a",{class:"header-anchor",href:"#why-peacock","aria-hidden":"true"},"#"),(0,a.Uk)(" Why Peacock")],-1),r=(0,a._)("p",null,"Have you ever found yourself switching between multiple instances of VS Code, while trying to find which one you were looking for? I often have multiple instances open for coding, writing, and pretty much everything I do with text. Being able to quickly identify each instance is super helpful.",-1),s=(0,a.Uk)("I used to switch the colors of a few key aspects of VS Code manually so I could differentiate them. I was using this technique at conferences where I presented and found it helpful for the audience so they could identify my code too. I finally decided to automate this. That's where "),d={href:"https://marketplace.visualstudio.com/items?itemName=johnpapa.vscode-peacock&wt.mc_id=vscodepeacock-github-jopapa",target:"_blank",rel:"noopener noreferrer"},h=(0,a.Uk)("Peacock"),l=(0,a.Uk)(" came from."),u=(0,a._)("h2",{id:"history-of-peacock",tabindex:"-1"},[(0,a._)("a",{class:"header-anchor",href:"#history-of-peacock","aria-hidden":"true"},"#"),(0,a.Uk)(" History of Peacock")],-1),p=(0,a.Uk)("A while back I created a theme for "),m={href:"https://code.visualstudio.com?wt.mc_id=vscodepeacock-github-jopapa",target:"_blank",rel:"noopener noreferrer"},k=(0,a.Uk)("Visual Studio Code"),f=(0,a.Uk)(" called "),y={href:"https://marketplace.visualstudio.com/items?itemName=johnpapa.winteriscoming&wt.mc_id=vscodepeacock-github-jopapa",target:"_blank",rel:"noopener noreferrer"},w=(0,a.Uk)("Winter is Coming"),g=(0,a.Uk)(". I learned how to do this by reading this "),b={href:"https://code.visualstudio.com/api/extension-capabilities/theming?wt.mc_id=vscodepeacock-github-jopapa",target:"_blank",rel:"noopener noreferrer"},v=(0,a.Uk)("great guide on theming"),_=(0,a.Uk)(" in the VS Code docs. I still use it today (I love the dark versions). It taught me a lot about how to customize colors in VS Code."),I=(0,a._)("p",null,"I then started using this to help solve another problem I had - quickly and visually differentiating between VS Code instances",-1),U=(0,a._)("p",null,"I'm often working on multiple code projects an articles I am writing in markdown. My workflow is to open different separate VS Code instances for each, as they are often unrelated to each other. As my mind shifts between the work, I find myself cycling through the instances and it takes me a bit of time to identify which instance I want to in focus.",-1),S=(0,a._)("p",null,"I decided to automate this. Once I had a working extension completed, I created a simple animated gif and shared it on twitter to see if anyone else was interested. Then I went to bed. I woke up he next morning to a lot of positive reactions than from the community (thank you). I also received many great contributions already (thank you again!).",-1),C=(0,a.Uk)("If you are interested in trying out Peacock, you can "),V={href:"https://marketplace.visualstudio.com/items?itemName=johnpapa.vscode-peacock&wt.mc_id=vscodepeacock-github-jopapa",target:"_blank",rel:"noopener noreferrer"},j=(0,a.Uk)("find it here in the marketplace"),x=(0,a.Uk)("."),P={},W=(0,o(3744).Z)(P,[["render",function(e,t){const o=(0,a.up)("OutboundLink");return(0,a.wg)(),(0,a.iD)(a.HY,null,[i,n,c,r,(0,a._)("p",null,[s,(0,a._)("a",d,[h,(0,a.Wm)(o)]),l]),u,(0,a._)("p",null,[p,(0,a._)("a",m,[k,(0,a.Wm)(o)]),f,(0,a._)("a",y,[w,(0,a.Wm)(o)]),g,(0,a._)("a",b,[v,(0,a.Wm)(o)]),_]),I,U,S,(0,a._)("p",null,[C,(0,a._)("a",V,[j,(0,a.Wm)(o)]),x])],64)}]])},3744:(e,t)=>{t.Z=(e,t)=>{for(const[o,a]of t)e[o]=a;return e}}}]); -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/named-colors.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/dist/assets/named-colors.gif -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/peacock-3-instances.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/dist/assets/peacock-3-instances.gif -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/peacock-icon-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/dist/assets/peacock-icon-small.png -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/peacock-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/dist/assets/peacock-icon.png -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/peacock-live-share-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/dist/assets/peacock-live-share-demo.gif -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/peacock-remote.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/dist/assets/peacock-remote.gif -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/peacock-sketchnote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/dist/assets/peacock-sketchnote.png -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/peacock-windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/dist/assets/peacock-windows.png -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/title-bar-coloring-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/dist/assets/title-bar-coloring-settings.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/affected-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/public/assets/affected-settings.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/element-adjustments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/public/assets/element-adjustments.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/public/assets/hero.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/named-colors.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/public/assets/named-colors.gif -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/peacock-3-instances.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/public/assets/peacock-3-instances.gif -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/peacock-icon-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/public/assets/peacock-icon-small.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/peacock-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/public/assets/peacock-icon.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/peacock-live-share-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/public/assets/peacock-live-share-demo.gif -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/peacock-remote.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/public/assets/peacock-remote.gif -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/peacock-sketchnote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/public/assets/peacock-sketchnote.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/peacock-windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/public/assets/peacock-windows.png -------------------------------------------------------------------------------- /docs/.vuepress/public/assets/title-bar-coloring-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/docs/.vuepress/public/assets/title-bar-coloring-settings.png -------------------------------------------------------------------------------- /docs/.vuepress/styles/index.scss: -------------------------------------------------------------------------------- 1 | :root { 2 | // brand colors 3 | --c-brand: #00b3e6; 4 | --c-brand-light: #65ceff; 5 | 6 | // background colors 7 | --c-bg: #ffffff; 8 | --c-bg-light: #f3f4f5; 9 | --c-bg-lighter: #eeeeee; 10 | --c-bg-navbar: var(--c-bg); 11 | --c-bg-sidebar: var(--c-bg); 12 | --c-bg-arrow: #cccccc; 13 | 14 | // text colors 15 | --c-text: #2c3e50; 16 | --c-text-accent: var(--c-brand); 17 | --c-text-light: #3a5169; 18 | --c-text-lighter: #4e6e8e; 19 | --c-text-lightest: #6a8bad; 20 | --c-text-quote: #999999; 21 | 22 | // border colors 23 | --c-border: #eaecef; 24 | --c-border-dark: #dfe2e5; 25 | 26 | // custom container colors 27 | --c-tip: #00b3e6; 28 | --c-tip-bg: var(--c-bg-light); 29 | --c-tip-title: var(--c-text); 30 | --c-tip-text: var(--c-text); 31 | --c-tip-text-accent: var(--c-text-accent); 32 | --c-warning: #e7c000; 33 | --c-warning-bg: #fffae3; 34 | --c-warning-title: #ad9000; 35 | --c-warning-text: #746000; 36 | --c-warning-text-accent: var(--c-text); 37 | --c-danger: #cc0000; 38 | --c-danger-bg: #ffe0e0; 39 | --c-danger-title: #990000; 40 | --c-danger-text: #660000; 41 | --c-danger-text-accent: var(--c-text); 42 | --c-details-bg: #eeeeee; 43 | 44 | // badge component colors 45 | --c-badge-tip: var(--c-tip); 46 | --c-badge-warning: var(--c-warning); 47 | --c-badge-danger: var(--c-danger); 48 | 49 | // transition vars 50 | --t-color: 0.3s ease; 51 | --t-transform: 0.3s ease; 52 | 53 | // code blocks vars 54 | --code-bg-color: #282c34; 55 | --code-hl-bg-color: rgba(0, 0, 0, 0.66); 56 | --code-ln-color: #9e9e9e; 57 | --code-ln-wrapper-width: 3.5rem; 58 | 59 | // font vars 60 | --font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 61 | 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; 62 | --font-family-code: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 63 | 64 | // layout vars 65 | --navbar-height: 3.6rem; 66 | --navbar-padding-v: 0.7rem; 67 | --navbar-padding-h: 1.5rem; 68 | --sidebar-width: 20rem; 69 | --sidebar-width-mobile: calc(var(--sidebar-width) * 0.82); 70 | --content-width: 740px; 71 | --homepage-width: 960px; 72 | } 73 | 74 | // plugin-back-to-top 75 | .back-to-top { 76 | --back-to-top-color: var(--c-brand); 77 | --back-to-top-color-hover: var(--c-brand-light); 78 | } 79 | 80 | // plugin-docsearch 81 | .DocSearch { 82 | --docsearch-primary-color: var(--c-brand); 83 | --docsearch-text-color: var(--c-text); 84 | --docsearch-highlight-color: var(--c-brand); 85 | --docsearch-muted-color: var(--c-text-quote); 86 | --docsearch-container-background: rgba(9, 10, 17, 0.8); 87 | --docsearch-modal-background: var(--c-bg-light); 88 | --docsearch-searchbox-background: var(--c-bg-lighter); 89 | --docsearch-searchbox-focus-background: var(--c-bg); 90 | --docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-brand); 91 | --docsearch-hit-color: var(--c-text-light); 92 | --docsearch-hit-active-color: var(--c-bg); 93 | --docsearch-hit-background: var(--c-bg); 94 | --docsearch-hit-shadow: 0 1px 3px 0 var(--c-border-dark); 95 | --docsearch-footer-background: var(--c-bg); 96 | } 97 | 98 | // plugin-medium-zoom 99 | .medium-zoom-overlay { 100 | --medium-zoom-bg-color: var(--c-bg); 101 | } 102 | 103 | // plugin-nprogress 104 | #nprogress { 105 | --nprogress-color: var(--c-brand); 106 | } 107 | 108 | // plugin-pwa-popup 109 | .pwa-popup { 110 | --pwa-popup-text-color: var(--c-text); 111 | --pwa-popup-bg-color: var(--c-bg); 112 | --pwa-popup-border-color: var(--c-brand); 113 | --pwa-popup-shadow: 0 4px 16px var(--c-brand); 114 | --pwa-popup-btn-text-color: var(--c-bg); 115 | --pwa-popup-btn-bg-color: var(--c-brand); 116 | --pwa-popup-btn-hover-bg-color: var(--c-brand-light); 117 | } 118 | 119 | // plugin-search 120 | .search-box { 121 | --search-bg-color: var(--c-bg); 122 | --search-accent-color: var(--c-brand); 123 | --search-text-color: var(--c-text); 124 | --search-border-color: var(--c-border); 125 | 126 | --search-item-text-color: var(--c-text-lighter); 127 | --search-item-focus-bg-color: var(--c-bg-light); 128 | } 129 | 130 | html.dark { 131 | // brand colors 132 | --c-brand: #00b3e6; 133 | --c-brand-light: #65ceff; 134 | 135 | // background colors 136 | --c-bg: #22272e; 137 | --c-bg-light: #2b313a; 138 | --c-bg-lighter: #262c34; 139 | 140 | // text colors 141 | --c-text: #adbac7; 142 | --c-text-light: #96a7b7; 143 | --c-text-lighter: #8b9eb0; 144 | --c-text-lightest: #8094a8; 145 | 146 | // border colors 147 | --c-border: #3e4c5a; 148 | --c-border-dark: #34404c; 149 | 150 | // custom container colors 151 | --c-tip: #318a62; 152 | --c-warning: #ceab00; 153 | --c-warning-bg: #7e755b; 154 | --c-warning-title: #ceac03; 155 | --c-warning-text: #362e00; 156 | --c-danger: #940000; 157 | --c-danger-bg: #806161; 158 | --c-danger-title: #610000; 159 | --c-danger-text: #3a0000; 160 | --c-details-bg: #323843; 161 | 162 | // code blocks vars 163 | --code-hl-bg-color: #363b46; 164 | } 165 | 166 | // plugin-docsearch 167 | html.dark .DocSearch { 168 | --docsearch-logo-color: var(--c-text); 169 | --docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309; 170 | --docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d, 171 | 0 2px 2px 0 rgba(3, 4, 9, 0.3); 172 | --docsearch-key-gradient: linear-gradient(-225deg, #444950, #1c1e21); 173 | --docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, 0.5), 0 -4px 8px 0 rgba(0, 0, 0, 0.2); 174 | } 175 | -------------------------------------------------------------------------------- /docs/.vuepress/styles/palette.scss: -------------------------------------------------------------------------------- 1 | $accentColor: #00b3e6; 2 | $hoverColor: #00b3e6; 3 | $textColor: #111; 4 | $borderColor: #00b3e6; 5 | $borderAccentColor: #00b3e6; 6 | $codeBgColor: #011627; 7 | $arrowBgColor: #00b3e6; 8 | 9 | //2c3e50 10 | 11 | header.navbar, 12 | header.navbar .links, 13 | header.navbar .search-box { 14 | border-bottom: $borderColor 1px solid; 15 | } 16 | 17 | span.site-name.can-hide, 18 | .nav-item, 19 | a.repo-link, 20 | a.nav-link, 21 | a.nav-link.router-link-active { 22 | svg.icon.outbound { 23 | &:hover { 24 | color: $hoverColor; 25 | } 26 | } 27 | } 28 | a.nav-link { 29 | &:hover { 30 | color: $hoverColor; 31 | } 32 | } 33 | 34 | .page .theme-default-content:not(.custom), 35 | .page .theme-default-content { 36 | h2, 37 | h3, 38 | h4 { 39 | border-left: 16px solid $borderColor; 40 | border-bottom: 1px solid $borderColor; 41 | padding: 2px 16px 6px 8px; 42 | a.header-anchor { 43 | padding-left: 16px; 44 | } 45 | margin-top: 48px; 46 | } 47 | h3 { 48 | border-left: 8px solid $borderColor; 49 | padding-left: 8px; 50 | } 51 | h4 { 52 | border-left: 4px solid $borderColor; 53 | padding-left: 0; 54 | } 55 | } 56 | 57 | .sidebar-link.active { 58 | border-left: 8px $accentColor solid; 59 | } 60 | .sidebar-sub-headers a.sidebar-link { 61 | border-left: none; 62 | } 63 | 64 | .theme-default-content code { 65 | background-color: #00b3e622 !important; 66 | letter-spacing: 2px; 67 | } 68 | 69 | td { 70 | line-height: 22px; 71 | } 72 | 73 | .line-number { 74 | color: $accentColor; 75 | } 76 | 77 | @media (max-width: 719px) { 78 | .sidebar { 79 | .nav-links { 80 | span.site-name.can-hide, 81 | .nav-item, 82 | a.repo-link, 83 | a.nav-link, 84 | a.nav-link.router-link-active, 85 | svg.icon.outbound { 86 | color: $textColor; 87 | // &:hover { 88 | // color: $hoverColor; 89 | // } 90 | } 91 | } 92 | } 93 | 94 | .page-edit .edit-link a { 95 | color: #777; 96 | &::before { 97 | content: '{ '; 98 | } 99 | &::after { 100 | content: ' }'; 101 | } 102 | // &:hover { 103 | // color: $hoverColor; 104 | // } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | heroImage: assets/hero.png 4 | actions: 5 | - text: Get Started → 6 | link: /guide/ 7 | type: primary 8 | features: 9 | - title: Work More Efficiently 10 | details: Quickly identify each of your Visual Studio Code instances using your favorite colors 11 | - title: Remote Integration 12 | details: Color your Visual Studio Code editor uniquely when you are using the remote integration features. 13 | - title: Live Share 14 | details: Color your Visual Studio Code editor uniquely when you are in a Live Share session as a Guest or a Host 15 | footer: MIT Licensed | Copyright © 2019-present John Papa | current version 4.2.3 16 | --- 17 | -------------------------------------------------------------------------------- /docs/about/code_of_conduct.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Code of Conduct 3 | # We can even add meta tags to the page! This sets the keywords meta tag. 4 | # 5 | description: Code of Conduct guide for the Visual Studio Code Peacock extension 6 | meta: 7 | - name: keywords 8 | - content: vscode "visual studio code" peacock theme extension Code of Conduct 9 | --- 10 | # Contributor Covenant Code of Conduct 11 | 12 | ## Our Pledge 13 | 14 | In the interest of fostering an open and welcoming environment, we as 15 | contributors and maintainers pledge to making participation in our project and 16 | our community a harassment-free experience for everyone, regardless of age, body 17 | size, disability, ethnicity, sex characteristics, gender identity and expression, 18 | level of experience, education, socio-economic status, nationality, personal 19 | appearance, race, religion, or sexual identity and orientation. 20 | 21 | ## Our Standards 22 | 23 | Examples of behavior that contributes to creating a positive environment 24 | include: 25 | 26 | * Using welcoming and inclusive language 27 | * Being respectful of differing viewpoints and experiences 28 | * Gracefully accepting constructive criticism 29 | * Focusing on what is best for the community 30 | * Showing empathy towards other community members 31 | 32 | Examples of unacceptable behavior by participants include: 33 | 34 | * The use of sexualized language or imagery and unwelcome sexual attention or 35 | advances 36 | * Trolling, insulting/derogatory comments, and personal or political attacks 37 | * Public or private harassment 38 | * Publishing others' private information, such as a physical or electronic 39 | address, without explicit permission 40 | * Other conduct which could reasonably be considered inappropriate in a 41 | professional setting 42 | 43 | ## Our Responsibilities 44 | 45 | Project maintainers are responsible for clarifying the standards of acceptable 46 | behavior and are expected to take appropriate and fair corrective action in 47 | response to any instances of unacceptable behavior. 48 | 49 | Project maintainers have the right and responsibility to remove, edit, or 50 | reject comments, commits, code, wiki edits, issues, and other contributions 51 | that are not aligned to this Code of Conduct, or to ban temporarily or 52 | permanently any contributor for other behaviors that they deem inappropriate, 53 | threatening, offensive, or harmful. 54 | 55 | ## Scope 56 | 57 | This Code of Conduct applies both within project spaces and in public spaces 58 | when an individual is representing the project or its community. Examples of 59 | representing a project or community include using an official project e-mail 60 | address, posting via an official social media account, or acting as an appointed 61 | representative at an online or offline event. Representation of a project may be 62 | further defined and clarified by project maintainers. 63 | 64 | ## Enforcement 65 | 66 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 67 | reported by contacting the project team at john+github@johnpapa.net. All 68 | complaints will be reviewed and investigated and will result in a response that 69 | is deemed necessary and appropriate to the circumstances. The project team is 70 | obligated to maintain confidentiality with regard to the reporter of an incident. 71 | Further details of specific enforcement policies may be posted separately. 72 | 73 | Project maintainers who do not follow or enforce the Code of Conduct in good 74 | faith may face temporary or permanent repercussions as determined by other 75 | members of the project's leadership. 76 | 77 | ## Attribution 78 | 79 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 80 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 81 | 82 | [homepage]: https://www.contributor-covenant.org 83 | 84 | For answers to common questions about this code of conduct, see 85 | https://www.contributor-covenant.org/faq 86 | -------------------------------------------------------------------------------- /docs/about/contributing.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Contributing 3 | # We can even add meta tags to the page! This sets the keywords meta tag. 4 | # 5 | description: Conributions guide for the Visual Studio Code Peacock extension 6 | meta: 7 | - name: keywords 8 | - content: vscode "visual studio code" peacock theme extension contributions 9 | --- 10 | 11 | # Contributing 12 | 13 | We would love for you to contribute and help make it even better 14 | than it is today! As a contributor, here are the guidelines we would like you 15 | to follow: 16 | 17 | -[Code of Conduct](#coc) 18 | 19 | - [Issues and Bugs](#issue) 20 | - [Feature Requests](#feature) 21 | - [Submission Guidelines](#submit) 22 | 23 | ## Code of Conduct 24 | 25 | Help us keep this project open and inclusive.Please read and follow our [Code of Conduct](./code_of_conduct). 26 | 27 | ## Found an Issue? 28 | 29 | If you find a bug in the source code or a mistake in the documentation, you can help us by 30 | [submitting an issue](#submit-issue) to our [GitHub Repository](https://github.com/johnpapa/vscode-peacock). Even better, you can 31 | [submit a Pull Request](#submit-pr) with a fix. 32 | 33 | ## Want a Feature? 34 | 35 | You can _request_ a new feature by [submitting an issue](#submit-issue) to our [GitHub Repository](https://github.com/johnpapa/vscode-peacock).If you would like to _implement_ a new feature, please submit an issue with 36 | a proposal for your work first, to be sure that we can use it. 37 | 38 | - **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr). 39 | 40 | ## Submission Guidelines 41 | 42 | ### Submitting an Issue 43 | 44 | Before you submit an issue, search the archive, maybe your question was already answered. 45 | 46 | If your issue appears to be a bug, and hasn't been reported, open a new issue. 47 | Help us to maximize the effort we can spend fixing issues and adding new 48 | features, by not reporting duplicate issues.Providing the following information will increase the 49 | chances of your issue being dealt with quickly: 50 | 51 | - **Overview of the Issue** - if an error is being thrown a non- minified stack trace helps 52 | - **Version** - what version is affected (e.g. 0.1.2) 53 | - **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you 54 | - **Browsers and Operating System** - is this a problem with all browsers? 55 | - **Reproduce the Error** - provide a live example or a unambiguous set of steps 56 | - **Related Issues** - has a similar issue been reported before? 57 | - **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be 58 | causing the problem (line of code or commit) 59 | 60 | You can file new issues by providing the above information [here](https://github.com/johnpapa/vscode-peacock/issues/new). 61 | 62 | ### Submitting a Pull Request (PR) 63 | 64 | Before you submit your Pull Request (PR) consider the following guidelines: 65 | 66 | - Search [GitHub](https://github.com/johnpapa/vscode-peacock/pulls) for an open or closed PR 67 | that relates to your submission.You don't want to duplicate effort. 68 | 69 | - Make your changes in a new git fork: 70 | 71 | - Commit your changes using a descriptive commit message 72 | - Push your fork to GitHub: 73 | - In GitHub, send a pull request 74 | 75 | - If we suggest changes then: 76 | - Make the required updates. 77 | - Rebase your fork and force push to your GitHub repository (this will update your Pull Request): 78 | 79 | ```shell 80 | git rebase master -i 81 | git push -f 82 | ``` 83 | 84 | That's it! Thank you for your contribution! 85 | -------------------------------------------------------------------------------- /docs/about/history.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: About 3 | # We can even add meta tags to the page! This sets the keywords meta tag. 4 | # 5 | description: About the Visual Studio Code and the Peacock Extension 6 | meta: 7 | - name: keywords 8 | - content: vscode "visual studio code" peacock theme extension 9 | --- 10 | 11 | # About Peacock 12 | 13 | Subtly change the color of your Visual Studio (VS) Code workspace. Ideal when you have multiple VS Code instances, use Visual Studio Live Share, or use VS Code's Remote features, and you want to quickly identify your editor. 14 | 15 | ## Why Peacock 16 | 17 | Have you ever found yourself switching between multiple instances of VS Code, while trying to find which one you were looking for? I often have multiple instances open for coding, writing, and pretty much everything I do with text. Being able to quickly identify each instance is super helpful. 18 | 19 | I used to switch the colors of a few key aspects of VS Code manually so I could differentiate them. I was using this technique at conferences where I presented and found it helpful for the audience so they could identify my code too. I finally decided to automate this. That's where [Peacock](https://marketplace.visualstudio.com/items?itemName=johnpapa.vscode-peacock&wt.mc_id=vscodepeacock-github-jopapa) came from. 20 | 21 | ## History of Peacock 22 | 23 | A while back I created a theme for [Visual Studio Code](https://code.visualstudio.com?wt.mc_id=vscodepeacock-github-jopapa) called [Winter is Coming](https://marketplace.visualstudio.com/items?itemName=johnpapa.winteriscoming&wt.mc_id=vscodepeacock-github-jopapa). I learned how to do this by reading this [great guide on theming](https://code.visualstudio.com/api/extension-capabilities/theming?wt.mc_id=vscodepeacock-github-jopapa) in the VS Code docs. I still use it today (I love the dark versions). It taught me a lot about how to customize colors in VS Code. 24 | 25 | I then started using this to help solve another problem I had - quickly and visually differentiating between VS Code instances 26 | 27 | I'm often working on multiple code projects an articles I am writing in markdown. My workflow is to open different separate VS Code instances for each, as they are often unrelated to each other. As my mind shifts between the work, I find myself cycling through the instances and it takes me a bit of time to identify which instance I want to in focus. 28 | 29 | I decided to automate this. Once I had a working extension completed, I created a simple animated gif and shared it on twitter to see if anyone else was interested. Then I went to bed. I woke up he next morning to a lot of positive reactions than from the community (thank you). I also received many great contributions already (thank you again!). 30 | 31 | If you are interested in trying out Peacock, you can [find it here in the marketplace](https://marketplace.visualstudio.com/items?itemName=johnpapa.vscode-peacock&wt.mc_id=vscodepeacock-github-jopapa). 32 | -------------------------------------------------------------------------------- /docs/about/license.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: License 3 | # We can even add meta tags to the page! This sets the keywords meta tag. 4 | # 5 | description: License for the Visual Studio Code Peacock extension 6 | meta: 7 | - name: keywords 8 | - content: vscode "visual studio code" peacock theme extension license 9 | --- 10 | MIT License 11 | 12 | Copyright (c) JohnPapa.net, LLC. All rights reserved. 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a copy 15 | of this software and associated documentation files (the "Software"), to deal 16 | in the Software without restriction, including without limitation the rights 17 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | copies of the Software, and to permit persons to whom the Software is 19 | furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in all 22 | copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | SOFTWARE 31 | -------------------------------------------------------------------------------- /resources/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/resources/hero.png -------------------------------------------------------------------------------- /resources/peacock-icon-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/resources/peacock-icon-small.png -------------------------------------------------------------------------------- /resources/peacock-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/vscode-peacock/d2c96693dc5c27c9b0271b5ac5b4fd2eef31c5ed/resources/peacock-icon.png -------------------------------------------------------------------------------- /src/apply-color.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | import { ColorSettings, extensionShortName, ISettingsIndexer } from './models'; 4 | import { 5 | getColorCustomizationConfigFromWorkspace, 6 | prepareColors, 7 | updateWorkspaceConfiguration, 8 | updatePeacockColor, 9 | updatePeacockRemoteColor, 10 | } from './configuration'; 11 | import { Logger } from './logging'; 12 | import { updateStatusBar } from './statusbar'; 13 | import { 14 | isValidColorInput, 15 | getBackgroundColorHex, 16 | deletePeacocksColorCustomizations, 17 | } from './color-library'; 18 | // import { ConfigurationTarget } from 'vscode'; 19 | 20 | export async function unapplyColors() { 21 | if (!vscode.workspace.workspaceFolders) { 22 | // If we are not in a workspace, don't allow Peacock to apply colors or write to settings. 23 | return; 24 | } 25 | 26 | // Overwite color customizations, without the peacock ones. 27 | // This preserves any extra ones someone might have. 28 | const colorCustomizationsWithPeacock = deletePeacocksColorCustomizations(); 29 | await updateWorkspaceConfiguration(colorCustomizationsWithPeacock); 30 | updateStatusBar(); 31 | } 32 | 33 | function mergeColorCustomizations( 34 | existingColors: ISettingsIndexer, 35 | updatedColors: ISettingsIndexer, 36 | ) { 37 | /** 38 | * Alays start with the existing colors. 39 | * So we clone existing into a new object that will contain 40 | * the merged (existing and updated) set of colors. 41 | */ 42 | const existingColorsClone: ISettingsIndexer = { ...existingColors }; 43 | 44 | /** 45 | * If any existing color settings are not in the set 46 | * that Peacock manages, remove them. 47 | */ 48 | Object.values(ColorSettings) 49 | .filter(c => !(c in updatedColors)) 50 | .forEach(c => delete existingColorsClone[c]); 51 | 52 | /** 53 | * Merge the updated colors on top of the existing colors. 54 | */ 55 | const mergedCustomizations: ISettingsIndexer = { 56 | ...existingColorsClone, 57 | ...updatedColors, 58 | }; 59 | 60 | return mergedCustomizations; 61 | } 62 | 63 | export async function applyColor(input: string) { 64 | /************************************************************** 65 | * This is the heart of Peacock logic to apply the colors. 66 | * 67 | */ 68 | 69 | if (!vscode.workspace.workspaceFolders) { 70 | // If we are not in a workspace, don't allow Peacock to apply colors or write to settings. 71 | return; 72 | } 73 | 74 | if (!isValidColorInput(input)) { 75 | await unapplyColors(); 76 | return; 77 | } 78 | 79 | const color = getBackgroundColorHex(input); 80 | 81 | // Get existing color customizations. 82 | const existingColors = getColorCustomizationConfigFromWorkspace(); 83 | 84 | // Get updated Peacock colors. 85 | const updatedColors = prepareColors(color); 86 | 87 | const colorCustomizations = mergeColorCustomizations(existingColors, updatedColors); 88 | 89 | await updateWorkspaceConfiguration(colorCustomizations); 90 | updateStatusBar(); 91 | 92 | Logger.info(`${extensionShortName}: Peacock is now using ${color}`); 93 | 94 | return color; 95 | } 96 | 97 | export async function updateColorSetting(color: string) { 98 | if (!vscode.workspace.workspaceFolders) { 99 | // If we are not in a workspace, don't allow Peacock to apply colors or write to settings. 100 | return; 101 | } 102 | 103 | if (!color) { 104 | return; 105 | } 106 | 107 | if (vscode.env.remoteName) { 108 | await updatePeacockRemoteColor(color); 109 | } else { 110 | await updatePeacockColor(color); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/color-library.ts: -------------------------------------------------------------------------------- 1 | import * as tinycolor from 'tinycolor2'; 2 | 3 | import { 4 | ColorAdjustment, 5 | ReadabilityRatios, 6 | inactiveElementAlpha, 7 | ColorSettings, 8 | ColorAdjustmentOptions, 9 | defaultAmountToDarkenLighten, 10 | defaultSaturation, 11 | } from './models'; 12 | import { 13 | getColorCustomizationConfigFromWorkspace, 14 | getDarkForegroundColorOrOverride, 15 | getLightForegroundColorOrOverride, 16 | } from './configuration'; 17 | 18 | export function getColorHex(color = '') { 19 | return formatHex(tinycolor(color)); 20 | } 21 | 22 | export function getBackgroundColorHex(color = '') { 23 | return formatHex(tinycolor(color)); 24 | } 25 | 26 | export function getInactiveBackgroundColorHex(backgroundColor = '') { 27 | const background = tinycolor(backgroundColor); 28 | background.setAlpha(inactiveElementAlpha); 29 | return formatHex(background); 30 | } 31 | 32 | export function getBackgroundHoverColorHex(backgroundColor = '') { 33 | const background = tinycolor(backgroundColor); 34 | const hoverColor = background.isLight() ? background.darken() : background.lighten(); 35 | return formatHex(hoverColor); 36 | } 37 | 38 | export function getForegroundColorHex(backgroundColor = '') { 39 | const background = tinycolor(backgroundColor); 40 | const foreground = background.isLight() 41 | ? getDarkForegroundColorOrOverride() 42 | : getLightForegroundColorOrOverride(); 43 | return formatHex(tinycolor(foreground)); 44 | } 45 | 46 | export function getInactiveForegroundColorHex(backgroundColor = '') { 47 | const foreground = tinycolor(getForegroundColorHex(backgroundColor)); 48 | foreground.setAlpha(inactiveElementAlpha); 49 | return formatHex(foreground); 50 | } 51 | 52 | export function getReadableAccentColorHex(backgroundColor = '', ratio = ReadabilityRatios.Text) { 53 | const background = tinycolor(backgroundColor); 54 | 55 | // Get an initial color for the badge as the first in a triad (120 degrees) 56 | // from the background color since it will be a more pleasing shade than 57 | // pure complementary (180 degrees). 58 | const foreground = background.triad()[1]; 59 | 60 | // Convert the color to HSL to work with the channels individually 61 | // eslint-disable-next-line prefer-const 62 | let { h, s, l } = foreground.toHsl(); 63 | 64 | // When there is no saturation we have some kind of grayscale color, 65 | // which for accent purposes should be colorized artificially 66 | if (s === 0) { 67 | // Spin the hue in the case of no saturation (grayscale). The spin is 68 | // deterministic based on the lightness of the color (0 to 1) and will 69 | // be mapped to one of the 6 primary and secondary hues (60 degree steps). 70 | h = 60 * Math.round(l * 6); 71 | } 72 | 73 | // Increase the saturation to 50% (defaultSaturation) 74 | // for any color that is very desaturated 75 | // to provide more of an accent in the manner that themes normally would 76 | if (s < 0.15) { 77 | s = defaultSaturation; 78 | } 79 | 80 | // Create an array of 16 shades of the accent color from no luminance 81 | // (black) to full luminance (white) and determine the contrast ratio 82 | // of each against the background. 83 | const shadeCount = 16; 84 | const shadeValue = 1 / shadeCount; 85 | const shadesWithRatios = [...Array(shadeCount).keys()].map(index => { 86 | const shade = tinycolor({ h, s, l: index * shadeValue }); 87 | return { 88 | contrast: tinycolor.readability(shade, background), 89 | hex: formatHex(shade), 90 | }; 91 | }); 92 | 93 | // Sort the shades by their contrast ratio from least to greatest so that 94 | // we can find the first shade that meets the readability threshold, but has 95 | // the least contrast of those that do. 96 | shadesWithRatios.sort((shade1, shade2) => shade1.contrast - shade2.contrast); 97 | const firstReadableShade = shadesWithRatios.find(shade => { 98 | return shade.contrast >= ratio; 99 | }); 100 | 101 | // Return the first readable shade that meets 102 | // the threshold or white if none of them do 103 | return firstReadableShade ? firstReadableShade.hex : '#ffffff'; 104 | } 105 | 106 | export function getBadgeBackgroundColorHex(backgroundColor = '') { 107 | return getReadableAccentColorHex(backgroundColor, ReadabilityRatios.UserInterfaceLow); 108 | } 109 | 110 | export function getDebuggingBackgroundColorHex(backgroundColor = '') { 111 | return formatHex(tinycolor(backgroundColor).complement()); 112 | } 113 | 114 | export function getAdjustedColorHex(color = '', adjustment: ColorAdjustment) { 115 | switch (adjustment) { 116 | case ColorAdjustmentOptions.lighten: 117 | return getLightenedColorHex(color); 118 | 119 | case ColorAdjustmentOptions.darken: 120 | return getDarkenedColorHex(color); 121 | 122 | default: 123 | return color; 124 | } 125 | } 126 | export function getLightenedColorHex(color = '', amount = defaultAmountToDarkenLighten) { 127 | return formatHex(tinycolor(color).lighten(amount)); 128 | } 129 | 130 | export function getDarkenedColorHex(color: string, amount = defaultAmountToDarkenLighten) { 131 | return formatHex(tinycolor(color).darken(amount)); 132 | } 133 | 134 | export function getRandomColorHex() { 135 | return formatHex(tinycolor.random()); 136 | } 137 | 138 | export function getColorBrightness(input = '') { 139 | return tinycolor(input).getBrightness(); 140 | } 141 | 142 | export function getColorComplementHex(input = '') { 143 | return formatHex(tinycolor(input).complement()); 144 | } 145 | 146 | export function getReadabilityRatio(backgroundColor = '', foregroundColor = '') { 147 | return tinycolor.readability(tinycolor(backgroundColor), tinycolor(foregroundColor)); 148 | } 149 | 150 | export function isValidColorInput(input: string) { 151 | const isValid = typeof input === 'string' && tinycolor(input).isValid(); 152 | return isValid; 153 | } 154 | 155 | export function deletePeacocksColorCustomizations() { 156 | const newColorCustomizations = getColorCustomizationConfigFromWorkspace(); 157 | 158 | Object.values(ColorSettings).forEach(setting => { 159 | delete newColorCustomizations[setting]; 160 | }); 161 | return newColorCustomizations; 162 | } 163 | 164 | function formatHex(color: tinycolor.Instance) { 165 | return color.getAlpha() < 1 ? color.toHex8String() : color.toHexString(); 166 | } 167 | -------------------------------------------------------------------------------- /src/commands.ts: -------------------------------------------------------------------------------- 1 | import { 2 | isValidColorInput, 3 | getRandomColorHex, 4 | getDarkenedColorHex, 5 | getLightenedColorHex, 6 | } from './color-library'; 7 | import { applyColor, unapplyColors, updateColorSetting } from './apply-color'; 8 | import { State, peacockGreen, docsUri } from './models'; 9 | import { 10 | getDarkenLightenPercentage, 11 | getRandomFavoriteColor, 12 | getSurpriseMeFromFavoritesOnly, 13 | addNewFavoriteColor, 14 | writeRecommendedFavoriteColors, 15 | updatePeacockColor, 16 | getEnvironmentAwareColor, 17 | updatePeacockRemoteColor, 18 | updatePeacockRemoteColorInUserSettings, 19 | updatePeacockColorInUserSettings, 20 | } from './configuration'; 21 | import { promptForColor, promptForFavoriteColor, promptForFavoriteColorName } from './inputs'; 22 | 23 | import { resetLiveSharePreviousColors } from './live-share'; 24 | import { notify } from './notification'; 25 | import * as vscode from 'vscode'; 26 | 27 | export async function removeAllPeacockColorsHandler() { 28 | await resetWorkspaceColorsHandler(); 29 | await updatePeacockColorInUserSettings(undefined); 30 | await updatePeacockRemoteColorInUserSettings(undefined); 31 | return State.extensionContext; 32 | } 33 | 34 | export async function showDocumentationHandler() { 35 | await vscode.env.openExternal(docsUri); 36 | return State.extensionContext; 37 | } 38 | 39 | export async function resetWorkspaceColorsHandler() { 40 | await resetLiveSharePreviousColors(); 41 | await updatePeacockColor(undefined); 42 | await updatePeacockRemoteColor(undefined); 43 | return State.extensionContext; 44 | } 45 | 46 | export async function saveColorToFavoritesHandler() { 47 | const color = getEnvironmentAwareColor(); 48 | if (color) { 49 | const name = await promptForFavoriteColorName(color); 50 | if (!name) { 51 | return; 52 | } 53 | await addNewFavoriteColor(name, color); 54 | } 55 | return State.extensionContext; 56 | } 57 | 58 | export async function enterColorHandler(color?: string) { 59 | const input = color ? color : await promptForColor(); 60 | if (!input) { 61 | return; 62 | } 63 | if (!isValidColorInput(input)) { 64 | throw new Error(`Invalid HEX or named color "${input}"`); 65 | } 66 | await applyColor(input); 67 | await updateColorSetting(input); 68 | return State.extensionContext; 69 | } 70 | 71 | export async function changeColorToRandomHandler() { 72 | const surpriseMeFromFavoritesOnly = getSurpriseMeFromFavoritesOnly(); 73 | let color = ''; 74 | 75 | if (surpriseMeFromFavoritesOnly) { 76 | const o = getRandomFavoriteColor(); 77 | if (!o) { 78 | notify( 79 | 'No favorites exist. Add some favorites if you want to use the surprise me from favorites feature', 80 | ); 81 | return State.extensionContext; 82 | } 83 | color = o.value; 84 | } else { 85 | color = getRandomColorHex(); 86 | } 87 | 88 | await applyColor(color); 89 | await updateColorSetting(color); 90 | return State.extensionContext; 91 | } 92 | 93 | export async function addRecommendedFavoritesHandler() { 94 | await writeRecommendedFavoriteColors(); 95 | return State.extensionContext; 96 | } 97 | 98 | export async function changeColorToPeacockGreenHandler() { 99 | await applyColor(peacockGreen); 100 | await updateColorSetting(peacockGreen); 101 | return State.extensionContext; 102 | } 103 | 104 | export async function changeColorToFavoriteHandler() { 105 | // Remember the color we started with 106 | const startingColor = getEnvironmentAwareColor(); 107 | const favoriteColor = await promptForFavoriteColor(); 108 | 109 | if (isValidColorInput(favoriteColor)) { 110 | // We have a valid Favorite color, 111 | // apply it and write the new color to settings 112 | await applyColor(favoriteColor); 113 | await updateColorSetting(favoriteColor); 114 | } else if (startingColor) { 115 | // No favorite was selected. 116 | // We need to re-apply the starting color 117 | // and write the new color to settings 118 | await applyColor(startingColor); 119 | await updateColorSetting(startingColor); 120 | } else { 121 | // No favorite was selected. We had no color to start, either. 122 | // We need re unapply the colors, and NOT write a color to settings. 123 | await unapplyColors(); 124 | } 125 | return State.extensionContext; 126 | } 127 | 128 | export async function darkenHandler() { 129 | const color = getEnvironmentAwareColor(); 130 | if (color) { 131 | const darkenLightenPercentage = getDarkenLightenPercentage(); 132 | const darkenedColor = getDarkenedColorHex(color, darkenLightenPercentage); 133 | await applyColor(darkenedColor); 134 | await updateColorSetting(darkenedColor); 135 | } 136 | return State.extensionContext; 137 | } 138 | 139 | export async function lightenHandler() { 140 | const color = getEnvironmentAwareColor(); 141 | if (color) { 142 | const darkenLightenPercentage = getDarkenLightenPercentage(); 143 | const lightenedColor = getLightenedColorHex(color, darkenLightenPercentage); 144 | await applyColor(lightenedColor); 145 | await updateColorSetting(lightenedColor); 146 | } 147 | return State.extensionContext; 148 | } 149 | 150 | export async function showAndCopyCurrentColorHandler() { 151 | const color = getEnvironmentAwareColor(); 152 | if (!color) { 153 | return; 154 | } 155 | const msg = color 156 | ? `Peacock's color is ${color} and has been copied to your clipboard.` 157 | : 'There is no Peacock color set at this time.'; 158 | vscode.env.clipboard.writeText(color); 159 | notify(msg, true); 160 | return State.extensionContext; 161 | } 162 | -------------------------------------------------------------------------------- /src/configuration/index.ts: -------------------------------------------------------------------------------- 1 | export * from './read-configuration'; 2 | export * from './update-configuration'; 3 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | // The module 'vscode' contains the VS Code extensibility API 2 | // Import the module and reference it with the alias vscode in your code below 3 | import * as vscode from 'vscode'; 4 | import { 5 | Commands, 6 | State, 7 | StandardSettings, 8 | extensionShortName, 9 | getExtensionVersion, 10 | ColorSource, 11 | } from './models'; 12 | import { 13 | resetWorkspaceColorsHandler, 14 | enterColorHandler, 15 | changeColorToRandomHandler, 16 | changeColorToPeacockGreenHandler, 17 | changeColorToFavoriteHandler, 18 | saveColorToFavoritesHandler, 19 | addRecommendedFavoritesHandler, 20 | darkenHandler, 21 | lightenHandler, 22 | showAndCopyCurrentColorHandler, 23 | removeAllPeacockColorsHandler, 24 | showDocumentationHandler, 25 | } from './commands'; 26 | import { 27 | checkIfPeacockSettingsChanged, 28 | getSurpriseMeOnStartup, 29 | writeRecommendedFavoriteColors, 30 | getEnvironmentAwareColor, 31 | inspectColor, 32 | getCurrentColorBeforeAdjustments, 33 | getFavoriteColors, 34 | } from './configuration'; 35 | import { applyColor, updateColorSetting } from './apply-color'; 36 | import { Logger } from './logging'; 37 | import { addLiveShareIntegration } from './live-share'; 38 | import { addRemoteIntegration } from './remote'; 39 | import { saveFavoritesVersionGlobalMemento, getMementos } from './mementos'; 40 | 41 | const { commands, workspace } = vscode; 42 | 43 | export async function activate(context: vscode.ExtensionContext) { 44 | State.extensionContext = context; 45 | // Logger.info(`${extensionShortName}: Extension "vscode-peacock" is now active!`); 46 | Logger.info(getMementos(), true, 'Peacock Mementos'); 47 | 48 | registerCommands(); 49 | await initializeTheStarterSetOfFavorites(); 50 | 51 | if (workspace.workspaceFolders) { 52 | Logger.info('Peacock is in a workspace, so Peacock functionality is available.'); 53 | /** 54 | * We only run this logic if we are in a workspace 55 | * because they may write peacock settings, and it will fail. 56 | * This entire function will re-run when a workspace is opened. 57 | */ 58 | await checkSurpriseMeOnStartupLogic(); 59 | await addLiveShareIntegration(State.extensionContext); 60 | await addRemoteIntegration(State.extensionContext); 61 | } else { 62 | Logger.info('Peacock is not in a workspace, so Peacock functionality is not available.'); 63 | } 64 | 65 | addSubscriptions(); // add these AFTER applying initial config 66 | } 67 | 68 | function addSubscriptions() { 69 | State.extensionContext.subscriptions.push(Logger.getChannel()); 70 | 71 | State.extensionContext.subscriptions.push(workspace.onDidChangeConfiguration(applyPeacock())); 72 | } 73 | 74 | function applyPeacock(): (e: vscode.ConfigurationChangeEvent) => any { 75 | return async e => { 76 | const color = getEnvironmentAwareColor(); 77 | const appliedColor = getCurrentColorBeforeAdjustments(); 78 | if (checkIfPeacockSettingsChanged(e) && (color || appliedColor)) { 79 | /** 80 | * If the settings have changed 81 | * AND (either we have a peacock.color/remoteColor to apply 82 | * OR we have an applied color already in the color customizations), 83 | * Then we apply the "color" 84 | */ 85 | Logger.info( 86 | `${extensionShortName}: Configuration changed. Changing the color to most recently selected color: ${color}`, 87 | ); 88 | await applyColor(color); 89 | 90 | // Only update the color in the workspace settings 91 | // if there was already a workspace setting 92 | const colorSource = inspectColor(); 93 | if (colorSource.colorSource === ColorSource.WorkspaceValue) { 94 | await updateColorSetting(color); 95 | } 96 | } 97 | }; 98 | } 99 | 100 | function registerCommands() { 101 | commands.registerCommand(Commands.showDocumentation, showDocumentationHandler); 102 | commands.registerCommand(Commands.resetWorkspaceColors, resetWorkspaceColorsHandler); 103 | commands.registerCommand(Commands.removeAllColors, removeAllPeacockColorsHandler); 104 | commands.registerCommand(Commands.saveColorToFavorites, saveColorToFavoritesHandler); 105 | commands.registerCommand(Commands.enterColor, enterColorHandler); 106 | commands.registerCommand(Commands.changeColorToRandom, changeColorToRandomHandler); 107 | commands.registerCommand(Commands.addRecommendedFavorites, addRecommendedFavoritesHandler); 108 | commands.registerCommand(Commands.changeColorToPeacockGreen, changeColorToPeacockGreenHandler); 109 | commands.registerCommand(Commands.changeColorToFavorite, changeColorToFavoriteHandler); 110 | commands.registerCommand(Commands.darken, darkenHandler); 111 | commands.registerCommand(Commands.lighten, lightenHandler); 112 | commands.registerCommand(Commands.showAndCopyCurrentColor, showAndCopyCurrentColorHandler); 113 | } 114 | 115 | export function deactivate() { 116 | // Logger.info(`${extensionShortName}: Extension "vscode-peacock" is now deactive`); 117 | } 118 | 119 | async function initializeTheStarterSetOfFavorites() { 120 | // If the version has changed, we write the current set of favorites to user settings.json, 121 | // merging them with any the user has created on their own 122 | const currentVersion = getExtensionVersion(); 123 | // TODO: Revisit how we merge favorites 124 | // For now we'll just add the starter set of favorites one time. 125 | // If there are favorites, do not write new ones. 126 | // We'll revisit this later so we do not overwrite favorites. 127 | const { values: favoritesValues } = getFavoriteColors(); 128 | if (!favoritesValues.length) { 129 | await writeRecommendedFavoriteColors(); 130 | await saveFavoritesVersionGlobalMemento(currentVersion); 131 | } 132 | } 133 | 134 | export async function checkSurpriseMeOnStartupLogic() { 135 | /** 136 | * If the "surprise me on startup" setting is true 137 | * and there is no peacock color set, then choose a new random color. 138 | * We do not choose a random color if there is already a color set 139 | * as this would confuse users who choose a specific color in a 140 | * workspace and see it changed to the "surprise" color 141 | */ 142 | const peacockColor = getEnvironmentAwareColor(); 143 | if (getSurpriseMeOnStartup()) { 144 | if (peacockColor) { 145 | const message = `Peacock did not change the color using "surprise me on startup" because the color ${peacockColor} was already set.`; 146 | Logger.info(message); 147 | return; 148 | } 149 | 150 | await changeColorToRandomHandler(); 151 | const color = getEnvironmentAwareColor(); 152 | const message = `Peacock changed the color to ${color}, because the setting is enabled for ${StandardSettings.SurpriseMeOnStartup}`; 153 | Logger.info(message); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/inputs.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { favoriteColorSeparator, peacockGreen } from './models'; 3 | import { getFavoriteColors } from './configuration'; 4 | import { applyColor } from './apply-color'; 5 | 6 | export async function promptForColor() { 7 | const options: vscode.InputBoxOptions = { 8 | ignoreFocusOut: true, 9 | placeHolder: peacockGreen, 10 | prompt: 11 | 'Enter a background color for the title bar in RGB hex format or a valid HTML color name', 12 | value: peacockGreen, 13 | }; 14 | const inputColor = (await vscode.window.showInputBox(options)) || ''; 15 | return inputColor.trim(); 16 | } 17 | 18 | export async function promptForFavoriteColorName(color: string) { 19 | if (!color) { 20 | return; 21 | } 22 | const options: vscode.InputBoxOptions = { 23 | ignoreFocusOut: true, 24 | placeHolder: 'Mandalorian Blue', 25 | prompt: `Enter a name for the color ${color}`, 26 | value: '', 27 | }; 28 | const inputName = await vscode.window.showInputBox(options); 29 | return inputName || ''; 30 | } 31 | 32 | export async function promptForFavoriteColor() { 33 | const { menu, values: favoriteColors } = getFavoriteColors(); 34 | let selection = ''; 35 | const options = { 36 | placeHolder: 'Pick a favorite color', 37 | onDidSelectItem: await tryColorWithPeacock(), 38 | }; 39 | if (favoriteColors && favoriteColors.length) { 40 | selection = (await vscode.window.showQuickPick(menu, options)) || ''; 41 | } 42 | if (selection) { 43 | const selectedColor = parseFavoriteColorValue(selection); 44 | return selectedColor || ''; 45 | } 46 | 47 | return ''; 48 | } 49 | 50 | export function parseFavoriteColorValue(text: string) { 51 | const sep = favoriteColorSeparator; 52 | return text.substring(text.indexOf(sep) + sep.length + 1); 53 | } 54 | 55 | async function tryColorWithPeacock() { 56 | return async (item: string) => { 57 | const color = parseFavoriteColorValue(item); 58 | return await applyColor(color); 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /src/live-share/enums.ts: -------------------------------------------------------------------------------- 1 | /* istanbul ignore file */ 2 | export enum LiveShareCommands { 3 | changeColorOfLiveShareHost = 'peacock.changeColorOfLiveShareHost', 4 | changeColorOfLiveShareGuest = 'peacock.changeColorOfLiveShareGuest', 5 | } 6 | 7 | export enum LiveShareSettings { 8 | VSLSShareColor = 'vslsShareColor', 9 | VSLSJoinColor = 'vslsJoinColor', 10 | } 11 | -------------------------------------------------------------------------------- /src/live-share/index.ts: -------------------------------------------------------------------------------- 1 | /* istanbul ignore file */ 2 | export * from './enums'; 3 | export * from './integration'; 4 | export * from './liveshare-commands'; 5 | -------------------------------------------------------------------------------- /src/live-share/integration.ts: -------------------------------------------------------------------------------- 1 | /* istanbul ignore file */ 2 | import * as vsls from 'vsls'; 3 | import * as vscode from 'vscode'; 4 | 5 | import { applyColor } from '../apply-color'; 6 | import { registerLiveShareIntegrationCommands } from './liveshare-commands'; 7 | import { State } from '../models'; 8 | import { notify } from '../notification'; 9 | import { LiveShareSettings } from './enums'; 10 | import { 11 | getLiveShareColor, 12 | getColorCustomizationConfigFromWorkspace, 13 | updateWorkspaceConfiguration, 14 | } from '../configuration'; 15 | 16 | let peacockColorCustomizations: any; 17 | 18 | export async function revertLiveShareWorkspaceColors() { 19 | await updateWorkspaceConfiguration(peacockColorCustomizations); 20 | 21 | peacockColorCustomizations = null; 22 | } 23 | 24 | async function setLiveShareSessionWorkspaceColors(isHost: boolean) { 25 | const colorSettingName = isHost 26 | ? LiveShareSettings.VSLSShareColor 27 | : LiveShareSettings.VSLSJoinColor; 28 | 29 | const liveShareColorSetting = getLiveShareColor(colorSettingName); 30 | if (!liveShareColorSetting) { 31 | return; 32 | } 33 | 34 | await applyColor(liveShareColorSetting); 35 | } 36 | 37 | export async function refreshLiveShareSessionColor(isHostRole: boolean): Promise { 38 | const vslsApi = await vsls.getApi(); 39 | 40 | // not in Live Share session, no need to update 41 | if (!vslsApi || !vslsApi.session.id) { 42 | const verb = isHostRole ? 'host and share' : 'join'; 43 | 44 | notify(`The selected color will be applied every time you ${verb} a Live Share session.`, true); 45 | 46 | return false; 47 | } 48 | 49 | const isHost = vslsApi.session.role === vsls.Role.Host; 50 | await setLiveShareSessionWorkspaceColors(isHost); 51 | return true; 52 | } 53 | 54 | export async function addLiveShareIntegration(context: vscode.ExtensionContext) { 55 | State.extensionContext = context; 56 | 57 | registerLiveShareIntegrationCommands(); 58 | 59 | const vslsApi = await vsls.getApi(); 60 | await vscode.commands.executeCommand('setContext', 'peacock:liveshare', !!vslsApi); 61 | 62 | if (!vslsApi) { 63 | return; 64 | } 65 | 66 | vslsApi!.onDidChangeSession(async e => { 67 | // If there isn't a session ID, then that 68 | // means the session has been ended. 69 | if (!e.session.id) { 70 | return await revertLiveShareWorkspaceColors(); 71 | } 72 | 73 | // we need to update `peacockColorCustomizations` only when it is `undefined` 74 | // to prevent the case of multiple color changes during live share session 75 | peacockColorCustomizations = await getColorCustomizationConfigFromWorkspace(); 76 | 77 | const isHost = e.session.role === vsls.Role.Host; 78 | return await setLiveShareSessionWorkspaceColors(isHost); 79 | }); 80 | } 81 | -------------------------------------------------------------------------------- /src/live-share/liveshare-commands.ts: -------------------------------------------------------------------------------- 1 | /* istanbul ignore file */ 2 | import { commands } from 'vscode'; 3 | 4 | import { promptForFavoriteColor } from '../inputs'; 5 | import { isValidColorInput } from '../color-library'; 6 | import { applyColor } from '../apply-color'; 7 | import { LiveShareCommands, LiveShareSettings } from './enums'; 8 | import { refreshLiveShareSessionColor, revertLiveShareWorkspaceColors } from './integration'; 9 | import { updateLiveShareColor, getEnvironmentAwareColor } from '../configuration'; 10 | import { State } from '../models'; 11 | 12 | const changeColorOfLiveShareSessionFactory = (isHost: boolean) => { 13 | return async function changeColorOfLiveShareSession() { 14 | const startingColor = getEnvironmentAwareColor(); 15 | const input = await promptForFavoriteColor(); 16 | 17 | if (isValidColorInput(input)) { 18 | const settingName = isHost 19 | ? LiveShareSettings.VSLSShareColor 20 | : LiveShareSettings.VSLSJoinColor; 21 | 22 | await updateLiveShareColor(settingName, input); 23 | } 24 | 25 | const isRefreshed = await refreshLiveShareSessionColor(isHost); 26 | // we are in the session and have updated the color, so return 27 | if (isRefreshed) { 28 | return State.extensionContext; 29 | } 30 | // if there is was no color prior to the color picker, 31 | // revert all the color settings 32 | if (!startingColor) { 33 | await revertLiveShareWorkspaceColors(); 34 | return State.extensionContext; 35 | // if there was a color set prior to color picker, 36 | // set that color back 37 | } else { 38 | await applyColor(startingColor); 39 | } 40 | 41 | return State.extensionContext; 42 | }; 43 | }; 44 | 45 | export const changeColorOfLiveShareHostHandler = changeColorOfLiveShareSessionFactory(true); 46 | export const changeColorOfLiveShareGuestHandler = changeColorOfLiveShareSessionFactory(false); 47 | 48 | export function registerLiveShareIntegrationCommands() { 49 | commands.registerCommand( 50 | LiveShareCommands.changeColorOfLiveShareHost, 51 | changeColorOfLiveShareHostHandler, 52 | ); 53 | commands.registerCommand( 54 | LiveShareCommands.changeColorOfLiveShareGuest, 55 | changeColorOfLiveShareGuestHandler, 56 | ); 57 | } 58 | 59 | export async function resetLiveSharePreviousColors() { 60 | await updateLiveShareColor(LiveShareSettings.VSLSShareColor, undefined); 61 | await updateLiveShareColor(LiveShareSettings.VSLSJoinColor, undefined); 62 | } 63 | -------------------------------------------------------------------------------- /src/live-share/test/runTest.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | 3 | import { runTests } from 'vscode-test'; 4 | 5 | async function main() { 6 | try { 7 | // The folder containing the Extension Manifest package.json 8 | // Passed to `--extensionDevelopmentPath` 9 | const extensionDevelopmentPath = path.resolve(__dirname, '../../../'); 10 | 11 | // The path to test runner 12 | // Passed to --extensionTestsPath 13 | const extensionTestsPath = path.resolve(__dirname, './suite/index'); 14 | 15 | // Download VS Code, unzip it and run the integration test 16 | await runTests({ 17 | extensionDevelopmentPath, 18 | extensionTestsPath, 19 | launchArgs: [ 20 | './testworkspace', 21 | // '--disable-extensions' 22 | // '${workspaceFolder}/testworkspace' 23 | ], 24 | }); 25 | } catch (err) { 26 | console.error('Failed to run tests'); 27 | process.exit(1); 28 | } 29 | } 30 | 31 | main(); 32 | -------------------------------------------------------------------------------- /src/live-share/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as Mocha from 'mocha'; 3 | import * as glob from 'glob'; 4 | 5 | export function run(): Promise { 6 | // Create the mocha test 7 | const mocha = new Mocha({ 8 | ui: 'tdd', 9 | //---------------------------------------- 10 | // Stuff from old test setup 11 | timeout: 200000, // longer timeout, in case 12 | // useColors: true, // colored output from test results 13 | //---------------------------------------- 14 | }); 15 | // mocha.useColors(true); 16 | 17 | const testsRoot = path.resolve(__dirname, '..'); 18 | 19 | return new Promise((c, e) => { 20 | glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { 21 | if (err) { 22 | return e(err); 23 | } 24 | 25 | // Add files to the test suite 26 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 27 | 28 | try { 29 | // Run the mocha test 30 | mocha.run((failures: any) => { 31 | if (failures > 0) { 32 | e(new Error(`${failures} tests failed.`)); 33 | } else { 34 | c(); 35 | } 36 | }); 37 | } catch (err) { 38 | e(err); 39 | } 40 | }); 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /src/logging.ts: -------------------------------------------------------------------------------- 1 | import { OutputChannel, window } from 'vscode'; 2 | 3 | export class Logger { 4 | private static _outputChannel: OutputChannel; 5 | 6 | static initialize() { 7 | if (!this._outputChannel) { 8 | // Only init once 9 | this._outputChannel = window.createOutputChannel('Peacock'); 10 | } 11 | } 12 | 13 | static getChannel() { 14 | this.initialize(); 15 | return this._outputChannel; 16 | } 17 | 18 | static info(value: string | object | undefined, indent = false, title = '') { 19 | if (title) { 20 | this._outputChannel.appendLine(title); 21 | } 22 | const message = prepareMessage(value, indent); 23 | this._outputChannel.appendLine(message); 24 | } 25 | } 26 | 27 | function prepareMessage(value: string | object | undefined, indent: boolean) { 28 | const prefix = indent ? ' ' : ''; 29 | let text = ''; 30 | if (typeof value === 'object') { 31 | if (Array.isArray(value)) { 32 | text = `${prefix}${JSON.stringify(value, null, 2)}`; 33 | } else { 34 | Object.entries(value).map(item => { 35 | text += `${prefix}${item[0]} = ${item[1]}\n`; 36 | }); 37 | } 38 | return text; 39 | } 40 | text = `${prefix}${value}`; 41 | return text; 42 | } 43 | 44 | Logger.initialize(); 45 | -------------------------------------------------------------------------------- /src/mementos.ts: -------------------------------------------------------------------------------- 1 | import { peacockMementos, extensionShortName, State } from './models'; 2 | import { Logger } from './logging'; 3 | 4 | export interface IMementoLog { 5 | name: string; 6 | type: 'workspaceState' | 'globalState'; 7 | value: any; 8 | } 9 | 10 | async function saveGlobalMemento(mementoName: string, value: any) { 11 | if (mementoName) { 12 | Logger.info( 13 | `${extensionShortName}: Saving the globalState ${mementoName} memento with value ${value}`, 14 | ); 15 | await State.extensionContext.globalState.update(mementoName, value); 16 | } 17 | } 18 | 19 | export async function saveFavoritesVersionGlobalMemento(version: string) { 20 | saveGlobalMemento(peacockMementos.favoritesVersion, version); 21 | } 22 | 23 | export function getFavoritesVersionGlobalMemento() { 24 | return State.extensionContext.globalState.get(peacockMementos.favoritesVersion, ''); 25 | } 26 | 27 | export async function resetFavoritesVersionMemento() { 28 | const ec = State.extensionContext; 29 | 30 | Logger.info( 31 | `${extensionShortName}: Setting all workspaceState and globalState mementos to undefined`, 32 | ); 33 | 34 | // Global 35 | await ec.globalState.update(peacockMementos.favoritesVersion, undefined); 36 | } 37 | 38 | export function getMementos() { 39 | const ec = State.extensionContext; 40 | const mementos: IMementoLog[] = []; 41 | 42 | // Globals 43 | mementos.push({ 44 | name: peacockMementos.favoritesVersion, 45 | type: 'globalState', 46 | value: ec.globalState.get(peacockMementos.favoritesVersion), 47 | }); 48 | 49 | return mementos; 50 | } 51 | -------------------------------------------------------------------------------- /src/models/constants.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | export const extensionShortName = 'peacock'; 4 | export const extensionId = 'johnpapa.vscode-peacock'; 5 | export const favoriteColorSeparator = '->'; 6 | 7 | export const docsUri = vscode.Uri.parse('https://www.peacockcode.dev'); 8 | 9 | // Matches the default inactive alpha in VS Code of 0x99 10 | // represented in 0-1 range for tinycolor.setAlpha() 11 | export const inactiveElementAlpha = 0x99 / 0xff; 12 | 13 | export const defaultAmountToDarkenLighten = 10; 14 | 15 | export const defaultSaturation = 0.5; 16 | 17 | export const azureBlue = '#007fff'; 18 | export const peacockGreen = '#42b883'; 19 | 20 | export const peacockMementos = { 21 | favoritesVersion: `${extensionShortName}.favoritesVersion`, 22 | }; 23 | 24 | export const timeout = async (ms = 200) => new Promise(resolve => setTimeout(resolve, ms)); 25 | 26 | export const isObjectEmpty = (o: {} | undefined) => 27 | typeof o === 'object' && Object.keys(o).length === 0; 28 | -------------------------------------------------------------------------------- /src/models/enums.ts: -------------------------------------------------------------------------------- 1 | import { LiveShareSettings } from '../live-share'; 2 | 3 | export enum StandardSettings { 4 | Color = 'color', 5 | DarkenLightenPercentage = 'darkenLightenPercentage', 6 | DarkForegroundColor = 'darkForegroundColor', 7 | ElementAdjustments = 'elementAdjustments', 8 | FavoriteColors = 'favoriteColors', 9 | KeepBadgeColor = 'keepBadgeColor', 10 | KeepForegroundColor = 'keepForegroundColor', 11 | LightForegroundColor = 'lightForegroundColor', 12 | RemoteColor = 'remoteColor', 13 | ShowColorInStatusBar = 'showColorInStatusBar', 14 | SquigglyBeGone = 'squigglyBeGone', 15 | SurpriseMeFromFavoritesOnly = 'surpriseMeFromFavoritesOnly', 16 | SurpriseMeOnStartup = 'surpriseMeOnStartup', 17 | } 18 | 19 | export enum AffectedSettings { 20 | EditorGroupBorder = 'affectEditorGroupBorder', 21 | PanelBorder = 'affectPanelBorder', 22 | SideBarBorder = 'affectSideBarBorder', 23 | SashHover = 'affectSashHover', 24 | ActivityBar = 'affectActivityBar', 25 | DebuggingStatusBar = 'affectDebuggingStatusBar', 26 | StatusBar = 'affectStatusBar', 27 | StatusAndTitleBorders = 'affectStatusAndTitleBorders', 28 | TabActiveBorder = 'affectTabActiveBorder', 29 | TitleBar = 'affectTitleBar', 30 | } 31 | 32 | export type AllSettings = StandardSettings | AffectedSettings | LiveShareSettings; 33 | 34 | export enum Commands { 35 | addRecommendedFavorites = 'peacock.addRecommendedFavorites', 36 | changeColorToRandom = 'peacock.changeColorToRandom', 37 | changeColorToPeacockGreen = 'peacock.changeColorToPeacockGreen', 38 | changeColorToFavorite = 'peacock.changeColorToFavorite', 39 | darken = 'peacock.darken', 40 | enterColor = 'peacock.enterColor', 41 | lighten = 'peacock.lighten', 42 | removeAllColors = 'peacock.removeAllColors', 43 | resetWorkspaceColors = 'peacock.resetWorkspaceColors', 44 | saveColorToFavorites = 'peacock.saveColorToFavorites', 45 | showAndCopyCurrentColor = 'peacock.showAndCopyCurrentColor', 46 | showDocumentation = 'peacock.docs', 47 | } 48 | 49 | export enum ElementNames { 50 | activityBar = 'activityBar', 51 | statusBar = 'statusBar', 52 | titleBar = 'titleBar', 53 | } 54 | 55 | export enum ColorSettings { 56 | activityBar_activeBackground = 'activityBar.activeBackground', 57 | activityBar_background = 'activityBar.background', 58 | activityBar_foreground = 'activityBar.foreground', 59 | activityBar_inactiveForeground = 'activityBar.inactiveForeground', 60 | activityBar_badgeBackground = 'activityBarBadge.background', 61 | activityBar_badgeForeground = 'activityBarBadge.foreground', 62 | commandCenter_border = 'commandCenter.border', 63 | editorGroupBorder = 'editorGroup.border', 64 | panelBorder = 'panel.border', 65 | sideBarBorder = 'sideBar.border', 66 | sashHover = 'sash.hoverBorder', 67 | squigglyBeGone_error = 'editorError.foreground', 68 | squigglyBeGone_warning = 'editorWarning.foreground', 69 | squigglyBeGone_info = 'editorInfo.foreground', 70 | statusBar_border = 'statusBar.border', 71 | statusBar_background = 'statusBar.background', 72 | statusBar_foreground = 'statusBar.foreground', 73 | statusBar_debuggingBorder = 'statusBar.debuggingBorder', 74 | statusBar_debuggingBackground = 'statusBar.debuggingBackground', 75 | statusBar_debuggingForeground = 'statusBar.debuggingForeground', 76 | statusBarItem_hoverBackground = 'statusBarItem.hoverBackground', 77 | statusBarItem_remoteBackground = 'statusBarItem.remoteBackground', 78 | statusBarItem_remoteForeground = 'statusBarItem.remoteForeground', 79 | tabActiveBorder = 'tab.activeBorder', 80 | titleBar_activeBackground = 'titleBar.activeBackground', 81 | titleBar_activeForeground = 'titleBar.activeForeground', 82 | titleBar_border = 'titleBar.border', 83 | titleBar_inactiveBackground = 'titleBar.inactiveBackground', 84 | titleBar_inactiveForeground = 'titleBar.inactiveForeground', 85 | } 86 | 87 | export type ColorAdjustment = 'lighten' | 'darken' | 'none'; 88 | 89 | export enum ColorAdjustmentOptions { 90 | lighten = 'lighten', 91 | darken = 'darken', 92 | none = 'none', 93 | } 94 | 95 | export enum Sections { 96 | peacockColorCustomizationSection = 'workbench.colorCustomizations', 97 | peacockSection = 'peacock', 98 | } 99 | 100 | export enum ForegroundColors { 101 | DarkForeground = '#15202b', 102 | LightForeground = '#e7e7e7', 103 | } 104 | 105 | // See WebAIM contrast guidelines: https://webaim.org/articles/contrast/ 106 | export enum ReadabilityRatios { 107 | UserInterfaceLow = 2, 108 | UserInterface = 3, 109 | Text = 4.5, 110 | } 111 | 112 | export enum ColorSource { 113 | WorkspaceValue = 'workspaceValue', 114 | GlobalValue = 'globalValue', 115 | DefaultValue = 'defaultValue', 116 | None = 'none', 117 | } 118 | -------------------------------------------------------------------------------- /src/models/favorites.ts: -------------------------------------------------------------------------------- 1 | export const starterSetOfFavorites = [ 2 | { name: 'Angular Red', value: '#dd0531' }, 3 | { name: 'Azure Blue', value: '#007fff' }, 4 | { name: 'JavaScript Yellow', value: '#f9e64f' }, 5 | { name: 'Mandalorian Blue', value: '#1857a4' }, 6 | { name: 'Node Green', value: '#215732' }, 7 | { name: 'React Blue', value: '#61dafb' }, 8 | { name: 'Something Different', value: '#832561' }, 9 | { name: 'Svelte Orange', value: '#ff3d00' }, 10 | { name: 'Vue Green', value: '#42b883' }, 11 | ]; 12 | -------------------------------------------------------------------------------- /src/models/index.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { extensionId } from './constants'; 3 | 4 | export * from './constants'; 5 | export * from './enums'; 6 | export * from './favorites'; 7 | export * from './interfaces'; 8 | export * from './state'; 9 | 10 | export function getExtension() { 11 | let extension: vscode.Extension | undefined; 12 | const ext = vscode.extensions.getExtension(extensionId); 13 | if (!ext) { 14 | throw new Error('Extension was not found.'); 15 | } 16 | if (ext) { 17 | extension = ext; 18 | } 19 | return extension; 20 | } 21 | -------------------------------------------------------------------------------- /src/models/interfaces.ts: -------------------------------------------------------------------------------- 1 | import { ColorAdjustment, ElementNames } from './enums'; 2 | 3 | export interface ISettingsIndexer { 4 | [key: string]: any; 5 | } 6 | 7 | export interface IFavoriteColors { 8 | name: string; 9 | value: string; 10 | } 11 | 12 | export interface ICommand { 13 | title: string; 14 | command: string; 15 | category: string; 16 | } 17 | 18 | export interface IConfiguration { 19 | type: string; 20 | title: string; 21 | properties: any; 22 | } 23 | 24 | export interface IElementStyle { 25 | backgroundHex: string; 26 | backgroundHoverHex: string; 27 | foregroundHex: string; 28 | inactiveBackgroundHex: string; 29 | inactiveForegroundHex: string; 30 | badgeBackgroundHex?: string; 31 | badgeForegroundHex?: string; 32 | } 33 | 34 | export interface IPeacockAffectedElementSettings { 35 | activityBar: boolean; 36 | statusBar: boolean; 37 | debuggingStatusBar: boolean; 38 | titleBar: boolean; 39 | editorGroupBorder: boolean; 40 | panelBorder: boolean; 41 | sideBarBorder: boolean; 42 | sashHover: boolean; 43 | statusAndTitleBorders: boolean; 44 | tabActiveBorder: boolean; 45 | } 46 | 47 | export interface IPeacockElementAdjustments { 48 | [elementName: string]: ColorAdjustment; 49 | } 50 | 51 | export interface IPeacockSettings { 52 | affectedElements: IPeacockAffectedElementSettings; 53 | editorGroupBorder: boolean; 54 | panelBorder: boolean; 55 | sideBarBorder: boolean; 56 | sashHover: boolean; 57 | affectStatusAndTitleBorders: boolean; 58 | elementAdjustments: IPeacockElementAdjustments; 59 | favoriteColors: IFavoriteColors[]; 60 | keepBadgeColor: boolean; 61 | keepForegroundColor: boolean; 62 | darkForegroundColor: string; 63 | lightForegroundColor: string; 64 | darkenLightenPercentage: number; 65 | showColorInStatusBar: boolean; 66 | squigglyBeGone: boolean; 67 | surpriseMeFromFavoritesOnly: boolean; 68 | surpriseMeOnStartup: boolean; 69 | color: string; 70 | remoteColor: string; 71 | } 72 | 73 | export interface IElementColors { 74 | [ElementNames.activityBar]: string; 75 | [ElementNames.statusBar]: string; 76 | [ElementNames.titleBar]: string; 77 | } 78 | -------------------------------------------------------------------------------- /src/models/state.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { getExtension } from './'; 3 | 4 | export class State { 5 | private static _extContext: vscode.ExtensionContext; 6 | 7 | public static get extensionContext(): vscode.ExtensionContext { 8 | return this._extContext; 9 | } 10 | 11 | public static set extensionContext(ec: vscode.ExtensionContext) { 12 | this._extContext = ec; 13 | } 14 | } 15 | 16 | export function getExtensionVersion() { 17 | const extension = getExtension(); 18 | const version: string = extension ? extension.packageJSON.version : ''; 19 | return version; 20 | } 21 | -------------------------------------------------------------------------------- /src/notification.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { Logger } from './logging'; 3 | 4 | export const notify = (message: string, log = false) => { 5 | vscode.window.showInformationMessage(message); 6 | if (log) { 7 | Logger.info(message); 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /src/object-library.ts: -------------------------------------------------------------------------------- 1 | import { ISettingsIndexer } from './models'; 2 | 3 | export function sortSettingsIndexer(unordered: ISettingsIndexer) { 4 | const ordered: ISettingsIndexer = {}; 5 | Object.keys(unordered) 6 | .sort() 7 | .forEach(key => (ordered[key] = unordered[key])); 8 | return ordered; 9 | } 10 | -------------------------------------------------------------------------------- /src/remote/enums.ts: -------------------------------------------------------------------------------- 1 | export enum RemoteNames { 2 | wsl = 'wsl', 3 | sshRemote = 'ssh-remote', 4 | devContainer = 'dev-container', 5 | } 6 | -------------------------------------------------------------------------------- /src/remote/index.ts: -------------------------------------------------------------------------------- 1 | export * from './enums'; 2 | export * from './integration'; 3 | -------------------------------------------------------------------------------- /src/remote/integration.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | import { applyColor } from '../apply-color'; 4 | import { State } from '../models'; 5 | import { getPeacockRemoteColor, getPeacockColor } from '../configuration'; 6 | 7 | // function remoteExtensionsInstalled(): boolean { 8 | // let remoteExtensions = [ 9 | // 'ms-vscode-remote.remote-containers', 10 | // 'ms-vscode-remote.remote-ssh', 11 | // 'ms-vscode-remote.remote-wsl', 12 | // ]; 13 | // return !!remoteExtensions.find(each => !!vscode.extensions.getExtension(each)); 14 | // } 15 | 16 | export async function addRemoteIntegration(context: vscode.ExtensionContext) { 17 | State.extensionContext = context; 18 | 19 | // const remoteExtensions = remoteExtensionsInstalled(); 20 | // await vscode.commands.executeCommand('setContext', 'peacock:remote', remoteExtensions); 21 | 22 | if (vscode.env.remoteName) { 23 | const remoteColor = getPeacockRemoteColor(); 24 | await applyColor(remoteColor); 25 | } else { 26 | const peacockColor = getPeacockColor(); 27 | await applyColor(peacockColor); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/statusbar.ts: -------------------------------------------------------------------------------- 1 | import { StatusBarAlignment, window, StatusBarItem } from 'vscode'; 2 | import { getShowColorInStatusBar, getEnvironmentAwareColor } from './configuration'; 3 | import { Commands } from './models'; 4 | 5 | const _statusBarItem: StatusBarItem = window.createStatusBarItem(StatusBarAlignment.Left); 6 | 7 | export const getStatusBarItem = () => { 8 | updateStatusBar(); 9 | return _statusBarItem; 10 | }; 11 | 12 | export function clearStatusBar() { 13 | const sb = _statusBarItem; 14 | sb.text = ''; 15 | sb.hide(); 16 | } 17 | 18 | export function updateStatusBar() { 19 | const sb = _statusBarItem; 20 | const show = getShowColorInStatusBar(); 21 | const color = getEnvironmentAwareColor(); 22 | sb.text = `$(paintcan) ${color}`; 23 | sb.command = Commands.showAndCopyCurrentColor; 24 | sb.tooltip = 'Copy the Peacock color'; 25 | if (show && !!color) { 26 | sb.show(); 27 | } else { 28 | clearStatusBar(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/coverage.ts: -------------------------------------------------------------------------------- 1 | import * as iLibInstrument from 'istanbul-lib-instrument'; 2 | import * as iLibCoverage from 'istanbul-lib-coverage'; 3 | import * as iLibSourceMaps from 'istanbul-lib-source-maps'; 4 | import * as iLibReport from 'istanbul-lib-report'; 5 | import * as iReports from 'istanbul-reports'; 6 | 7 | import * as fs from 'fs'; 8 | import * as path from 'path'; 9 | 10 | const REPO_ROOT = path.resolve(__dirname, '../..'); 11 | 12 | export function instrument() { 13 | const instrumenter = iLibInstrument.createInstrumenter(); 14 | const files = rreaddir(path.resolve(REPO_ROOT, 'out')); 15 | 16 | for (let i = 0; i < files.length; i++) { 17 | const file = files[i]; 18 | 19 | if (/\.js\.map$/.test(file)) { 20 | // console.log(`ignoring ${file}`); 21 | continue; 22 | } 23 | 24 | const inputPath = path.resolve(REPO_ROOT, 'out', files[i]); 25 | const outputPath = path.resolve(REPO_ROOT, 'out-cov', files[i]); 26 | 27 | if (!/\.js$/.test(file) || /(^|[\\/])test[\\/]/.test(file)) { 28 | // console.log(`copying ${inputPath}`); 29 | copyFile(inputPath, outputPath); 30 | continue; 31 | } 32 | 33 | // Try to find a .map file 34 | let map = null; 35 | try { 36 | map = JSON.parse(fs.readFileSync(`${inputPath}.map`).toString()); 37 | } catch (err) { 38 | // missing source map... 39 | } 40 | 41 | // console.log(`instrumenting ${inputPath}...`); 42 | const instrumentedCode = instrumenter.instrumentSync( 43 | fs.readFileSync(inputPath).toString(), 44 | inputPath, 45 | map, 46 | ); 47 | safeWriteFile(outputPath, instrumentedCode); 48 | } 49 | } 50 | 51 | export function createReport(): void { 52 | const global = new Function('return this')(); 53 | 54 | const mapStore = iLibSourceMaps.createSourceMapStore(); 55 | const coverageMap = iLibCoverage.createCoverageMap(global.__coverage__); 56 | const transformed = mapStore.transformCoverage(coverageMap); 57 | 58 | const watermarks = { 59 | statements: [50, 80], 60 | functions: [50, 80], 61 | branches: [50, 80], 62 | lines: [50, 80], 63 | }; 64 | 65 | const tree = iLibReport.summarizers.flat(transformed.map); 66 | const context = iLibReport.createContext({ 67 | dir: path.resolve(REPO_ROOT, `coverage`), 68 | watermarks, 69 | }); 70 | 71 | const reports = [ 72 | iReports.create('json'), 73 | iReports.create('lcov'), 74 | iReports.create('html'), 75 | iReports.create('cobertura'), 76 | ]; 77 | reports.forEach(report => tree.visit(report, context)); 78 | } 79 | 80 | function copyFile(inputPath: string, outputPath: string): void { 81 | safeWriteFile(outputPath, fs.readFileSync(inputPath)); 82 | } 83 | 84 | function safeWriteFile(filePath: string, contents: Buffer | string): void { 85 | ensureDir(path.dirname(filePath)); 86 | fs.writeFileSync(filePath, contents); 87 | } 88 | 89 | function ensureDir(dirname: string): void { 90 | if (fs.existsSync(dirname)) { 91 | return; 92 | } 93 | ensureDir(path.dirname(dirname)); 94 | fs.mkdirSync(dirname); 95 | } 96 | 97 | function rreaddir(dirname: string): string[] { 98 | const result: string[] = []; 99 | _rreaddir(dirname, dirname, result); 100 | return result; 101 | } 102 | 103 | function _rreaddir(dirname: string, relativeTo: string, result: string[]): void { 104 | const entries = fs.readdirSync(dirname); 105 | for (let i = 0; i < entries.length; i++) { 106 | const entry = entries[i]; 107 | const entryPath = path.join(dirname, entry); 108 | if (fs.statSync(entryPath).isDirectory()) { 109 | _rreaddir(entryPath, relativeTo, result); 110 | } else { 111 | result.push(path.relative(relativeTo, entryPath)); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/test/runTest.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | 3 | import { runTests } from 'vscode-test'; 4 | 5 | import { instrument } from './coverage'; 6 | 7 | async function main() { 8 | try { 9 | // The folder containing the Extension Manifest package.json 10 | // Passed to `--extensionDevelopmentPath` 11 | const extensionDevelopmentPath = path.resolve(__dirname, '../../'); 12 | 13 | // The path to test runner 14 | // Passed to --extensionTestsPath 15 | let extensionTestsPath = path.resolve(__dirname, './suite/index'); 16 | 17 | if (process.argv.indexOf('--coverage') >= 0) { 18 | // generate instrumented files at out-cov 19 | instrument(); 20 | 21 | // load the instrumented files 22 | extensionTestsPath = path.resolve(__dirname, '../../out-cov/test/suite/index'); 23 | 24 | // signal that the coverage data should be gathered 25 | process.env['GENERATE_COVERAGE'] = '1'; 26 | } 27 | 28 | // Download VS Code, unzip it and run the integration test 29 | await runTests({ 30 | extensionDevelopmentPath, 31 | extensionTestsPath, 32 | launchArgs: [ 33 | './testworkspace', 34 | '--disable-extensions', 35 | // '${workspaceFolder}/testworkspace' 36 | ], 37 | }); 38 | } catch (err) { 39 | console.error('Failed to run tests'); 40 | process.exit(1); 41 | } 42 | } 43 | 44 | main(); 45 | -------------------------------------------------------------------------------- /src/test/suite/basic.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as assert from 'assert'; 3 | import { 4 | IPeacockSettings, 5 | ICommand, 6 | Commands, 7 | IConfiguration, 8 | StandardSettings, 9 | extensionShortName, 10 | AffectedSettings, 11 | getExtension, 12 | timeout, 13 | } from '../../models'; 14 | import { setupTestSuite, teardownTestSuite, setupTest } from './lib/setup-teardown-test-suite'; 15 | 16 | suite('Basic Extension Tests', () => { 17 | const originalValues = {} as IPeacockSettings; 18 | let extension: vscode.Extension; 19 | 20 | suiteSetup(async () => await setupTestSuite(originalValues)); 21 | suiteTeardown(async () => await teardownTestSuite(originalValues)); 22 | setup(async () => await setupTest()); 23 | 24 | suiteSetup(() => { 25 | extension = getExtension() as vscode.Extension; 26 | }); 27 | 28 | test('Sample Test', async () => { 29 | assert.equal(-1, [1, 2, 3].indexOf(5)); 30 | }); 31 | 32 | test('Activation test', async () => { 33 | await extension.activate(); 34 | assert.equal(extension.isActive, true); 35 | }); 36 | 37 | test('Extension loads in VSCode and is active', async () => { 38 | // Hopefully a timeout will allow the extension to activate within Windows 39 | // otherwise we get a false result. 40 | // let extension = getExtension() as vscode.Extension; 41 | 42 | await timeout(3000); 43 | assert.equal(extension.isActive, true); 44 | }); 45 | 46 | test('Commands exist in package.json', () => { 47 | // let extension = getExtension() as vscode.Extension; 48 | const commandCollection: ICommand[] = extension.packageJSON.contributes.commands; 49 | const indexedCommands: { [idx: string]: Commands } = Commands as any; 50 | for (const command in Commands) { 51 | const result = commandCollection.some(c => c.command === indexedCommands[command]); 52 | assert.ok(result); 53 | } 54 | }); 55 | 56 | test('Settings exist in package.json', () => { 57 | // let extension = getExtension() as vscode.Extension; 58 | 59 | const config: IConfiguration = extension.packageJSON.contributes.configuration; 60 | const properties = Object.keys(config.properties); 61 | const indexedSettings: { [idx: string]: StandardSettings } = StandardSettings as any; 62 | for (const setting in StandardSettings) { 63 | const result = properties.some( 64 | property => property === `${extensionShortName}.${indexedSettings[setting]}`, 65 | ); 66 | assert.ok(result); 67 | } 68 | }); 69 | 70 | test('AffectedSettings exist in package.json', () => { 71 | // let extension = getExtension() as vscode.Extension; 72 | const config: IConfiguration = extension.packageJSON.contributes.configuration; 73 | const properties = Object.keys(config.properties); 74 | const indexedSettings: { [idx: string]: AffectedSettings } = AffectedSettings as any; 75 | for (const setting in AffectedSettings) { 76 | const result = properties.some( 77 | property => property === `${extensionShortName}.${indexedSettings[setting]}`, 78 | ); 79 | assert.ok(result); 80 | } 81 | }); 82 | 83 | test('package.json commands registered in extension', done => { 84 | // let extension = getExtension() as vscode.Extension; 85 | 86 | const commandStrings: string[] = extension.packageJSON.contributes.commands.map( 87 | (c: ICommand) => c.command, 88 | ); 89 | 90 | vscode.commands.getCommands(true).then((allCommands: string[]) => { 91 | const commands = allCommands.filter(c => c.startsWith(`${extensionShortName}.`)); 92 | commands.forEach(command => { 93 | const result = commandStrings.some(c => c === command); 94 | assert.ok(result); 95 | }); 96 | done(); 97 | }); 98 | }); 99 | }); 100 | -------------------------------------------------------------------------------- /src/test/suite/built-in-colors.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as assert from 'assert'; 3 | import { 4 | Commands, 5 | ColorSettings, 6 | IPeacockSettings, 7 | peacockGreen, 8 | StandardSettings, 9 | } from '../../models'; 10 | import { setupTestSuite, teardownTestSuite, setupTest } from './lib/setup-teardown-test-suite'; 11 | import { executeCommand } from './lib/constants'; 12 | import { isValidColorInput } from '../../color-library'; 13 | import { 14 | getColorCustomizationConfig, 15 | updateWorkspaceConfiguration, 16 | getColorCustomizationConfigFromWorkspace, 17 | getFavoriteColors, 18 | updateSurpriseMeFromFavoritesOnly, 19 | getEnvironmentAwareColor, 20 | getPeacockWorkspace, 21 | } from '../../configuration'; 22 | 23 | suite('can set color to built-in color', () => { 24 | const originalValues = {} as IPeacockSettings; 25 | 26 | suiteSetup(async () => await setupTestSuite(originalValues)); 27 | suiteTeardown(async () => await teardownTestSuite(originalValues)); 28 | setup(async () => await setupTest()); 29 | 30 | test('can set color to Peacock Green', testChangingColorToPeacockGreen()); 31 | 32 | test('can set to color and it is stored in workspace config', async () => { 33 | await executeCommand(Commands.changeColorToRandom); 34 | const config = getPeacockWorkspace(); 35 | const color = config[StandardSettings.Color]; 36 | assert.ok(color); 37 | assert.ok(isValidColorInput(color)); 38 | }); 39 | 40 | suite('can set color to Random color', () => { 41 | test('color is valid', async () => { 42 | await executeCommand(Commands.changeColorToRandom); 43 | const config = getColorCustomizationConfig(); 44 | assert.ok(isValidColorInput(config[ColorSettings.titleBar_activeBackground])); 45 | }); 46 | 47 | suite( 48 | 'when surpriseMeFromFavoritesOnly is true, color matches a favorite and is not chosen at random', 49 | () => { 50 | const limit = 10; 51 | for (let index = 0; index < limit; index++) { 52 | test(`test run ${index} of ${limit}`, async () => { 53 | const { values: favorites } = getFavoriteColors(); 54 | await updateSurpriseMeFromFavoritesOnly(true); 55 | await executeCommand(Commands.changeColorToRandom); 56 | const color = getEnvironmentAwareColor(); 57 | const match = favorites.find(item => item.value.toLowerCase === color.toLowerCase); 58 | assert.ok( 59 | match, 60 | `chosen color ${color} is not found in the favorites ${JSON.stringify(favorites)}`, 61 | ); 62 | }); 63 | } 64 | }, 65 | ); 66 | }); 67 | 68 | suite('when resetting colors', () => { 69 | suiteSetup(async () => await setupTestSuite(originalValues)); 70 | suiteTeardown(async () => await teardownTestSuite(originalValues)); 71 | setup(async () => await setupTest()); 72 | 73 | const extraSettingName = 'activityBar.border'; 74 | const extraSettingValue = '#ff0'; 75 | const extraSetting = { 'activityBar.border': extraSettingValue }; 76 | 77 | test('leaves pre-existing colorCustomizations', async () => { 78 | await removeExtraSetting(extraSettingName); 79 | // Add one non Peacock setting 80 | await updateWorkspaceConfiguration(extraSetting); 81 | 82 | await executeCommand(Commands.resetWorkspaceColors); 83 | const config = getColorCustomizationConfig(); 84 | assert.equal(config[extraSettingName], extraSettingValue); 85 | assert.ok(!config[ColorSettings.titleBar_activeBackground]); 86 | assert.ok(!config[ColorSettings.statusBar_background]); 87 | assert.ok(!config[ColorSettings.activityBar_background]); 88 | assert.ok(!config[ColorSettings.activityBar_activeBackground]); 89 | 90 | await removeExtraSetting(extraSettingName); 91 | }); 92 | 93 | test('removes colorCustomizations if the object is empty', async () => { 94 | await executeCommand(Commands.resetWorkspaceColors); 95 | const config = getColorCustomizationConfig(); 96 | assert.ok(!config[ColorSettings.titleBar_activeBackground]); 97 | assert.ok(!config[ColorSettings.statusBar_background]); 98 | assert.ok(!config[ColorSettings.activityBar_background]); 99 | assert.ok(!config[ColorSettings.activityBar_activeBackground]); 100 | // assert.ok(!config[ColorSettings.activityBar_activeBorder]); 101 | }); 102 | 103 | test('removes peacockColor', async () => { 104 | await executeCommand(Commands.resetWorkspaceColors); 105 | const config = getPeacockWorkspace(); 106 | assert.ok(!config[StandardSettings.Color]); 107 | }); 108 | 109 | test('removes peacockRemoteColor', async () => { 110 | await executeCommand(Commands.resetWorkspaceColors); 111 | const config = getPeacockWorkspace(); 112 | assert.ok(!config[StandardSettings.RemoteColor]); 113 | }); 114 | }); 115 | }); 116 | 117 | function testChangingColorToPeacockGreen(): 118 | | ((this: Mocha.ITestCallbackContext, done: MochaDone) => any) 119 | | undefined { 120 | return testBuiltInColor(Commands.changeColorToPeacockGreen, peacockGreen); 121 | } 122 | 123 | function testBuiltInColor( 124 | cmd: Commands, 125 | builtInColor: string, 126 | ): ((this: Mocha.ITestCallbackContext, done: MochaDone) => any) | undefined { 127 | return async () => { 128 | await vscode.commands.executeCommand(cmd); 129 | const config = getColorCustomizationConfig(); 130 | assert.equal(builtInColor, config[ColorSettings.titleBar_activeBackground]); 131 | }; 132 | } 133 | 134 | async function removeExtraSetting(extraSettingName: string) { 135 | const newColorCustomizations: any = { 136 | ...getColorCustomizationConfigFromWorkspace(), 137 | }; 138 | delete newColorCustomizations[extraSettingName]; 139 | await updateWorkspaceConfiguration(newColorCustomizations); 140 | } 141 | -------------------------------------------------------------------------------- /src/test/suite/color-input.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as assert from 'assert'; 3 | import * as sinon from 'sinon'; 4 | import { ColorSettings, Commands, IPeacockSettings } from '../../models'; 5 | import { isValidColorInput } from '../../color-library'; 6 | import { setupTestSuite, teardownTestSuite, setupTest } from './lib/setup-teardown-test-suite'; 7 | import { executeCommand } from './lib/constants'; 8 | import { getColorCustomizationConfig } from '../../configuration'; 9 | 10 | suite('Enter color', () => { 11 | const originalValues = {} as IPeacockSettings; 12 | 13 | suiteSetup(async () => await setupTestSuite(originalValues)); 14 | suiteTeardown(async () => await teardownTestSuite(originalValues)); 15 | setup(async () => await setupTest()); 16 | 17 | suite('Invalid values do nothing', () => { 18 | test('can hit ESC with no error', createColorInputTest('', undefined)); 19 | 20 | test('can hit ENTER with no error', createColorInputTest('', undefined)); 21 | }); 22 | 23 | suite('Hex, Hex RGBA', () => { 24 | test('can set color using short hex user input', createColorInputTest('#000', '#000000')); 25 | 26 | test( 27 | 'can set color using short hex user input without hash', 28 | createColorInputTest('000', '#000000'), 29 | ); 30 | 31 | test( 32 | 'can set color using short RGBA hex user input', 33 | createColorInputTest('#369C', '#336699cc'), 34 | ); 35 | 36 | test( 37 | 'can set color using short RGBA hex user input without hash', 38 | createColorInputTest('369C', '#336699cc'), 39 | ); 40 | 41 | test('can set color using hex user input', createColorInputTest('#f0f0f6', '#f0f0f6')); 42 | 43 | test( 44 | 'can set color using hex user input without hash', 45 | createColorInputTest('f0f0f6', '#f0f0f6'), 46 | ); 47 | 48 | test('can set color using RGBA hex user input', createColorInputTest('#f0f0f688', '#f0f0f688')); 49 | 50 | test( 51 | 'can set color using RGBA hex user input without hash', 52 | createColorInputTest('f0f0f688', '#f0f0f688'), 53 | ); 54 | }); 55 | 56 | suite('Named colors', () => { 57 | test( 58 | 'can set color using named color user input', 59 | createColorInputTest('blanchedalmond', '#ffebcd'), 60 | ); 61 | 62 | test( 63 | 'can set color using named color user input with any casing', 64 | createColorInputTest('DarkBlue', '#00008b'), 65 | ); 66 | 67 | // RGB, RGBA 68 | 69 | test( 70 | 'can set color using rgb() color user input', 71 | createColorInputTest('rgb (255 0 0)', '#ff0000'), 72 | ); 73 | 74 | test( 75 | 'can set color using rgb() color user input without parentheses', 76 | createColorInputTest('rgb 255 0 0', '#ff0000'), 77 | ); 78 | 79 | test( 80 | 'can set color using rgba() color user input', 81 | createColorInputTest('rgba (255, 0, 0, .5)', '#ff000080'), 82 | ); 83 | 84 | test( 85 | 'can set color using rgb() color user input with decimals or percentages', 86 | createColorInputTest('rgb (100% 255 0)', '#ffff00'), 87 | ); 88 | }); 89 | 90 | suite('HSL, HSLA', () => { 91 | test( 92 | 'can set color using hsl() color user input', 93 | createColorInputTest('hsl (0 100% 50%)', '#ff0000'), 94 | ); 95 | 96 | test( 97 | 'can set color using hsl() color user input without parentheses', 98 | createColorInputTest('hsl 0 100% 50%', '#ff0000'), 99 | ); 100 | 101 | test( 102 | 'can set color using hsla() color user input', 103 | createColorInputTest('hsla (0, 100%, 50%, .5)', '#ff000080'), 104 | ); 105 | 106 | test( 107 | 'can set color using hsl() color user input with decimals or percentages', 108 | createColorInputTest('hsl (0, 100%, .5)', '#ff0000'), 109 | ); 110 | }); 111 | 112 | suite('HSV, HSVA', () => { 113 | test( 114 | 'can set color using hsv() color user input', 115 | createColorInputTest('hsv (0, 100%, 100%)', '#ff0000'), 116 | ); 117 | 118 | test( 119 | 'can set color using hsv() color user input without parentheses', 120 | createColorInputTest('hsv 0 100% 100%', '#ff0000'), 121 | ); 122 | 123 | test( 124 | 'can set color using hsva() color user input', 125 | createColorInputTest('hsva (0, 100%, 100%, .5)', '#ff000080'), 126 | ); 127 | 128 | test( 129 | 'can set color using hsv() color user input with decimals or percentages', 130 | createColorInputTest('hsv (0, 1, 100%)', '#ff0000'), 131 | ); 132 | }); 133 | 134 | suite('With Parameters', () => { 135 | test( 136 | 'can set valid color using command parameters programmatically', 137 | createColorInputTestWithParam('#c0c0c0', '#c0c0c0'), 138 | ); 139 | 140 | test( 141 | 'cannot set invalid color using command parameters programmatically', 142 | createColorInputTestWithParamThatThrowsError('invalid'), 143 | ); 144 | }); 145 | }); 146 | 147 | function createColorInputTest(fakeResponse: string, expectedValue: string | undefined) { 148 | return async () => { 149 | // Stub the async input box to return a response 150 | const stub = await sinon 151 | .stub(vscode.window, 'showInputBox') 152 | .returns(Promise.resolve(fakeResponse)); 153 | 154 | // fire the command 155 | await executeCommand(Commands.enterColor); 156 | const config = getColorCustomizationConfig(); 157 | const value = config[ColorSettings.titleBar_activeBackground]; 158 | stub.restore(); 159 | 160 | // undefined is OK, since that means they hit ESC or blank 161 | // Otherwise, we need a valid color 162 | assert.ok(!value || isValidColorInput(value)); 163 | assert.equal(expectedValue, value); 164 | }; 165 | } 166 | 167 | function createColorInputTestWithParam(fakeResponse: string, expectedValue: string | undefined) { 168 | return async () => { 169 | await executeCommand(Commands.enterColor, fakeResponse); 170 | 171 | const config = getColorCustomizationConfig(); 172 | const value = config[ColorSettings.titleBar_activeBackground]; 173 | 174 | // undefined is OK, since that means they hit ESC or blank 175 | // Otherwise, we need a valid color 176 | assert.ok(!value || isValidColorInput(value)); 177 | assert.equal(expectedValue, value); 178 | }; 179 | } 180 | 181 | function createColorInputTestWithParamThatThrowsError(fakeResponse: string) { 182 | return async () => { 183 | assert.rejects(async () => await executeCommand(Commands.enterColor, fakeResponse), Error); 184 | const config = getColorCustomizationConfig(); 185 | const value = config[ColorSettings.titleBar_activeBackground]; 186 | // The value should be undefined when invalid color is set 187 | assert.ok(!value); 188 | }; 189 | } 190 | -------------------------------------------------------------------------------- /src/test/suite/config-changes.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as assert from 'assert'; 3 | import { 4 | Commands, 5 | IPeacockSettings, 6 | AffectedSettings, 7 | IElementColors, 8 | ElementNames, 9 | IPeacockAffectedElementSettings, 10 | timeout, 11 | } from '../../models'; 12 | import { setupTestSuite, teardownTestSuite, setupTest } from './lib/setup-teardown-test-suite'; 13 | import { executeCommand } from './lib/constants'; 14 | import { 15 | getColorCustomizationConfigFromWorkspace, 16 | getOriginalColorsForAllElements, 17 | getUserConfig, 18 | updateAffectedElements, 19 | updateGlobalConfiguration, 20 | updateWorkspaceConfiguration, 21 | } from '../../configuration'; 22 | 23 | const delayInMs = 500; 24 | 25 | suite('changes to configuration', () => { 26 | const originalValues = {} as IPeacockSettings; 27 | 28 | suiteSetup(async () => await setupTestSuite(originalValues)); 29 | suiteTeardown(async () => await teardownTestSuite(originalValues)); 30 | setup(async () => await setupTest()); 31 | 32 | setup(async () => { 33 | // This suite's tests flips these switches a lot, 34 | // so we reset before each test just to be sure. 35 | await executeCommand(Commands.resetWorkspaceColors); 36 | // Set the test values 37 | await updateAffectedElements({ 38 | statusBar: true, 39 | activityBar: true, 40 | titleBar: true, 41 | } as IPeacockAffectedElementSettings); 42 | }); 43 | 44 | suite('when starting with no colors in the workspace config', () => { 45 | test('have no effect', async () => { 46 | const colors1: IElementColors = getOriginalColorsForAllElements(); 47 | const config1 = getUserConfig(); 48 | await updateGlobalConfiguration( 49 | AffectedSettings.ActivityBar, 50 | !config1[AffectedSettings.ActivityBar], 51 | ); 52 | 53 | await timeout(delayInMs); 54 | 55 | const colors2: IElementColors = getOriginalColorsForAllElements(); 56 | assert.ok(colors1[ElementNames.activityBar] === colors2[ElementNames.activityBar]); 57 | assert.ok( 58 | !!config1[AffectedSettings.StatusBar] && 59 | colors1[ElementNames.statusBar] === colors2[ElementNames.statusBar], 60 | ); 61 | assert.ok( 62 | !!config1[AffectedSettings.TitleBar] && 63 | colors1[ElementNames.titleBar] === colors2[ElementNames.titleBar], 64 | ); 65 | }); 66 | }); 67 | 68 | suite('when starting with a color in the workspace config', () => { 69 | setup(async () => { 70 | // Use Peacock Green as the color the instance began with 71 | await vscode.commands.executeCommand(Commands.changeColorToPeacockGreen); 72 | }); 73 | 74 | test('will change color when unselecting activitybar', async () => { 75 | const colorsBefore: IElementColors = getOriginalColorsForAllElements(); 76 | const configBefore = getUserConfig(); 77 | await updateGlobalConfiguration(AffectedSettings.ActivityBar, false); 78 | 79 | await timeout(delayInMs); 80 | 81 | const colorsAfter: IElementColors = getOriginalColorsForAllElements(); 82 | assert.ok(colorsBefore[ElementNames.activityBar] !== colorsAfter[ElementNames.activityBar]); 83 | assert.ok( 84 | !!configBefore[AffectedSettings.StatusBar] && 85 | colorsBefore[ElementNames.statusBar] === colorsAfter[ElementNames.statusBar], 86 | ); 87 | assert.ok( 88 | !!configBefore[AffectedSettings.TitleBar] && 89 | colorsBefore[ElementNames.titleBar] === colorsAfter[ElementNames.titleBar], 90 | ); 91 | }); 92 | 93 | test('will change color when unselecting statusbar', async () => { 94 | const colors1: IElementColors = getOriginalColorsForAllElements(); 95 | const config1 = getUserConfig(); 96 | await updateGlobalConfiguration( 97 | AffectedSettings.StatusBar, 98 | !config1[AffectedSettings.StatusBar], 99 | ); 100 | 101 | await timeout(delayInMs); 102 | 103 | const colors2: IElementColors = getOriginalColorsForAllElements(); 104 | assert.ok(colors1[ElementNames.statusBar] !== colors2[ElementNames.statusBar]); 105 | assert.ok( 106 | !!config1[AffectedSettings.ActivityBar] && 107 | colors1[ElementNames.activityBar] === colors2[ElementNames.activityBar], 108 | ); 109 | assert.ok( 110 | !!config1[AffectedSettings.TitleBar] && 111 | colors1[ElementNames.titleBar] === colors2[ElementNames.titleBar], 112 | ); 113 | }); 114 | 115 | test('will change color when unselecting titlebar', async () => { 116 | const config1 = getUserConfig(); 117 | const colors1: IElementColors = getOriginalColorsForAllElements(); 118 | await updateGlobalConfiguration( 119 | AffectedSettings.TitleBar, 120 | !config1[AffectedSettings.TitleBar], 121 | ); 122 | 123 | await timeout(delayInMs); 124 | const colors2: IElementColors = getOriginalColorsForAllElements(); 125 | assert.ok(colors1[ElementNames.titleBar] !== colors2[ElementNames.titleBar]); 126 | assert.ok( 127 | !!config1[AffectedSettings.ActivityBar] && 128 | colors1[ElementNames.activityBar] === colors2[ElementNames.activityBar], 129 | ); 130 | assert.ok( 131 | !!config1[AffectedSettings.StatusBar] && 132 | colors1[ElementNames.statusBar] === colors2[ElementNames.statusBar], 133 | ); 134 | }); 135 | 136 | test('will preserve setting order', async () => { 137 | const originalCustomizations = getColorCustomizationConfigFromWorkspace(); 138 | const keptKeys = Object.keys(originalCustomizations).filter((_, i) => i % 2); 139 | const removedKeys = Object.keys(originalCustomizations).filter((_, i) => ~i % 2); 140 | removedKeys.forEach(r => delete originalCustomizations[r]); 141 | await updateWorkspaceConfiguration(originalCustomizations); 142 | 143 | await executeCommand(Commands.changeColorToPeacockGreen); 144 | 145 | const updatedCustomizations = getColorCustomizationConfigFromWorkspace(); 146 | 147 | assert.deepEqual( 148 | Object.keys(updatedCustomizations), 149 | keptKeys.concat(removedKeys), 150 | 'existing setting order was not preserved', 151 | ); 152 | }); 153 | }); 154 | }); 155 | -------------------------------------------------------------------------------- /src/test/suite/current-color.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as sinon from 'sinon'; 3 | import * as assert from 'assert'; 4 | import { IPeacockSettings, Commands, peacockGreen, azureBlue } from '../../models'; 5 | import { setupTestSuite, teardownTestSuite, setupTest } from './lib/setup-teardown-test-suite'; 6 | import { executeCommand } from './lib/constants'; 7 | import { getEnvironmentAwareColor } from '../../configuration'; 8 | 9 | suite('Current Color Tests', () => { 10 | const originalValues = {} as IPeacockSettings; 11 | 12 | suiteSetup(async () => await setupTestSuite(originalValues)); 13 | suiteTeardown(async () => await teardownTestSuite(originalValues)); 14 | setup(async () => await setupTest()); 15 | 16 | suite('when running the "show color" command', async () => { 17 | test('Shows the current color', async () => { 18 | await executeCommand(Commands.changeColorToPeacockGreen); 19 | await executeCommand(Commands.showAndCopyCurrentColor); 20 | const color = getEnvironmentAwareColor(); 21 | assert.equal(color, peacockGreen); 22 | }); 23 | test('Shows the current color when it is a custom color', async () => { 24 | const fakeResponse = `Azure Blue -> ${azureBlue}`; 25 | const stub = await sinon 26 | .stub(vscode.window, 'showQuickPick') 27 | .returns(Promise.resolve(fakeResponse)); 28 | 29 | await executeCommand(Commands.changeColorToFavorite); 30 | const color = getEnvironmentAwareColor(); 31 | stub.restore(); 32 | 33 | await executeCommand(Commands.showAndCopyCurrentColor); 34 | assert.equal(color, azureBlue); 35 | }); 36 | test('Shows no color when none is set', async () => { 37 | await executeCommand(Commands.showAndCopyCurrentColor); 38 | const color = getEnvironmentAwareColor(); 39 | assert.equal(color, ''); 40 | }); 41 | test('Copies to the clipboard', async () => { 42 | await executeCommand(Commands.changeColorToPeacockGreen); 43 | await executeCommand(Commands.showAndCopyCurrentColor); 44 | const clipboardColor = await vscode.env.clipboard.readText(); 45 | assert.equal(clipboardColor, peacockGreen); 46 | }); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /src/test/suite/darken-lighten.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import { IPeacockSettings, Commands, ColorSettings, peacockGreen } from '../../models'; 3 | import { setupTestSuite, teardownTestSuite, setupTest } from './lib/setup-teardown-test-suite'; 4 | import { getColorCustomizationConfig, getDarkenLightenPercentage } from '../../configuration'; 5 | import { executeCommand } from './lib/constants'; 6 | import { getLightenedColorHex, getDarkenedColorHex } from '../../color-library'; 7 | 8 | suite('Darken/Lighten commands', () => { 9 | const originalValues = {} as IPeacockSettings; 10 | 11 | suiteSetup(async () => await setupTestSuite(originalValues)); 12 | suiteTeardown(async () => await teardownTestSuite(originalValues)); 13 | setup(async () => await setupTest()); 14 | 15 | test('can lighten a color', async () => { 16 | await executeCommand(Commands.changeColorToPeacockGreen); 17 | await executeCommand(Commands.lighten); 18 | const config = getColorCustomizationConfig(); 19 | const pct = getDarkenLightenPercentage(); 20 | 21 | assert.equal( 22 | getLightenedColorHex(peacockGreen, pct), 23 | config[ColorSettings.activityBar_background], 24 | ); 25 | }); 26 | 27 | test('can darken a color', async () => { 28 | await executeCommand(Commands.changeColorToPeacockGreen); 29 | await executeCommand(Commands.darken); 30 | const config = getColorCustomizationConfig(); 31 | const pct = getDarkenLightenPercentage(); 32 | 33 | assert.equal( 34 | getDarkenedColorHex(peacockGreen, pct), 35 | config[ColorSettings.activityBar_background], 36 | ); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /src/test/suite/documentation.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as sinon from 'sinon'; 3 | import * as assert from 'assert'; 4 | import { IPeacockSettings, Commands, docsUri } from '../../models'; 5 | import { setupTestSuite, teardownTestSuite, setupTest } from './lib/setup-teardown-test-suite'; 6 | import { executeCommand } from './lib/constants'; 7 | 8 | suite('Documentation Tests', () => { 9 | const originalValues = {} as IPeacockSettings; 10 | 11 | suiteSetup(async () => await setupTestSuite(originalValues)); 12 | suiteTeardown(async () => await teardownTestSuite(originalValues)); 13 | setup(async () => await setupTest()); 14 | 15 | // suiteSetup(async () => {}); 16 | 17 | test('can open documentation web site in a browser', async () => { 18 | const openExternalStub = sinon.stub(vscode.env, 'openExternal'); 19 | 20 | // Call the function to test. 21 | await executeCommand(Commands.showDocumentation); 22 | 23 | // Ensure it attempted to open the browser. 24 | assert.ok(openExternalStub.calledOnce); 25 | assert.ok(openExternalStub.calledOnceWithExactly(docsUri), 'wrong Uri was opened'); 26 | const wrongUri = vscode.Uri.parse('https://google.com'); 27 | assert.ok(!openExternalStub.calledOnceWithExactly(wrongUri)); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/test/suite/element-adjustments.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import { 3 | IPeacockSettings, 4 | IPeacockElementAdjustments, 5 | Commands, 6 | ColorSettings, 7 | IPeacockAffectedElementSettings, 8 | peacockGreen, 9 | } from '../../models'; 10 | import { setupTestSuite, teardownTestSuite, setupTest } from './lib/setup-teardown-test-suite'; 11 | import { 12 | updateElementAdjustments, 13 | getColorCustomizationConfig, 14 | updateAffectedElements, 15 | } from '../../configuration'; 16 | import { executeCommand, allAffectedElements } from './lib/constants'; 17 | import { getLightenedColorHex, getDarkenedColorHex, getColorBrightness } from '../../color-library'; 18 | 19 | suite('Element adjustments', () => { 20 | const originalValues = {} as IPeacockSettings; 21 | 22 | suiteSetup(async () => await setupTestSuite(originalValues)); 23 | suiteTeardown(async () => await teardownTestSuite(originalValues)); 24 | setup(async () => await setupTest()); 25 | 26 | const elementAdjustments: IPeacockElementAdjustments = { 27 | activityBar: 'lighten', 28 | statusBar: 'darken', 29 | titleBar: 'none', 30 | }; 31 | 32 | suiteSetup(async () => { 33 | await updateElementAdjustments(elementAdjustments); 34 | }); 35 | 36 | test('can lighten the color of an affected element', async () => { 37 | await executeCommand(Commands.changeColorToPeacockGreen); 38 | const config = getColorCustomizationConfig(); 39 | assert.equal(getLightenedColorHex(peacockGreen), config[ColorSettings.activityBar_background]); 40 | }); 41 | 42 | test('can darken the color of an affected element', async () => { 43 | await executeCommand(Commands.changeColorToPeacockGreen); 44 | const config = getColorCustomizationConfig(); 45 | assert.equal(getDarkenedColorHex(peacockGreen), config[ColorSettings.statusBar_background]); 46 | }); 47 | 48 | test('set adjustment to none for an affected element is noop', async () => { 49 | await executeCommand(Commands.changeColorToPeacockGreen); 50 | const config = getColorCustomizationConfig(); 51 | assert.equal(peacockGreen, config[ColorSettings.titleBar_activeBackground]); 52 | }); 53 | 54 | test('set adjustment to lighten for an affected element is lighter color', async () => { 55 | await executeCommand(Commands.changeColorToPeacockGreen); 56 | const config = getColorCustomizationConfig(); 57 | 58 | const originalBrightness = getColorBrightness(peacockGreen); 59 | const adjustedBrightness = getColorBrightness(config[ColorSettings.activityBar_background]); 60 | assert.ok( 61 | originalBrightness < adjustedBrightness, 62 | `Expected original brightness ${originalBrightness} to be less than ${adjustedBrightness}, but was greater`, 63 | ); 64 | }); 65 | 66 | test('set adjustment to darken for an affected element is darker color', async () => { 67 | await executeCommand(Commands.changeColorToPeacockGreen); 68 | const config = getColorCustomizationConfig(); 69 | 70 | const originalBrightness = getColorBrightness(peacockGreen); 71 | const adjustedBrightness = getColorBrightness(config[ColorSettings.statusBar_background]); 72 | assert.ok( 73 | originalBrightness > adjustedBrightness, 74 | `Expected original brightness ${originalBrightness} to be greater than ${adjustedBrightness}, but was less`, 75 | ); 76 | }); 77 | 78 | test('can adjust the color of an affected elements independently', async () => { 79 | await executeCommand(Commands.changeColorToPeacockGreen); 80 | const config = getColorCustomizationConfig(); 81 | assert.equal(getLightenedColorHex(peacockGreen), config[ColorSettings.activityBar_background]); 82 | assert.equal(getDarkenedColorHex(peacockGreen), config[ColorSettings.statusBar_background]); 83 | assert.equal(peacockGreen, config[ColorSettings.titleBar_activeBackground]); 84 | }); 85 | 86 | test('can only adjust the color of an element that is affected', async () => { 87 | await updateAffectedElements({ 88 | activityBar: false, 89 | statusBar: true, 90 | titleBar: false, 91 | } as IPeacockAffectedElementSettings); 92 | 93 | await executeCommand(Commands.changeColorToPeacockGreen); 94 | const config = getColorCustomizationConfig(); 95 | 96 | assert.equal(getDarkenedColorHex(peacockGreen), config[ColorSettings.statusBar_background]); 97 | assert.ok(!config[ColorSettings.activityBar_background]); 98 | assert.ok(!config[ColorSettings.titleBar_activeBackground]); 99 | 100 | await updateAffectedElements(allAffectedElements); 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /src/test/suite/favorite-colors.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as sinon from 'sinon'; 3 | import * as assert from 'assert'; 4 | import { IPeacockSettings, Commands, azureBlue } from '../../models'; 5 | import { setupTestSuite, teardownTestSuite, setupTest } from './lib/setup-teardown-test-suite'; 6 | import { parseFavoriteColorValue } from '../../inputs'; 7 | import { isValidColorInput } from '../../color-library'; 8 | import { executeCommand } from './lib/constants'; 9 | import { 10 | getFavoriteColors, 11 | updateFavoriteColors, 12 | getEnvironmentAwareColor, 13 | } from '../../configuration'; 14 | 15 | suite('Favorite colors', () => { 16 | const originalValues = {} as IPeacockSettings; 17 | 18 | suiteSetup(async () => await setupTestSuite(originalValues)); 19 | suiteTeardown(async () => await teardownTestSuite(originalValues)); 20 | setup(async () => await setupTest()); 21 | 22 | test('can set color to favorite color', async () => { 23 | // Stub the async quick pick to return a response 24 | const fakeResponse = `Azure Blue -> ${azureBlue}`; 25 | const stub = await sinon 26 | .stub(vscode.window, 'showQuickPick') 27 | .returns(Promise.resolve(fakeResponse)); 28 | 29 | await executeCommand(Commands.changeColorToFavorite); 30 | const color = getEnvironmentAwareColor(); 31 | stub.restore(); 32 | 33 | const parsedResponse = parseFavoriteColorValue(fakeResponse); 34 | 35 | assert.ok(isValidColorInput(color)); 36 | assert.ok(color === parsedResponse); 37 | }); 38 | 39 | test('set to favorite color with no preferences is a noop', async () => { 40 | // set the color to peacock green to start 41 | await executeCommand(Commands.changeColorToPeacockGreen); 42 | 43 | // Stub the async quick pick to return a response 44 | const fakeResponse = ''; 45 | const stub = await sinon 46 | .stub(vscode.window, 'showQuickPick') 47 | .returns(Promise.resolve(fakeResponse)); 48 | 49 | const valueBefore = getEnvironmentAwareColor(); 50 | await executeCommand(Commands.changeColorToFavorite); 51 | const valueAfter = getEnvironmentAwareColor(); 52 | stub.restore(); 53 | 54 | assert.ok(valueBefore === valueAfter); 55 | }); 56 | 57 | test('set to favorite color with no preferences is a noop, when color was not previously set', async () => { 58 | // Stub the async quick pick to return a response 59 | const fakeResponse = ''; 60 | const stub = await sinon 61 | .stub(vscode.window, 'showQuickPick') 62 | .returns(Promise.resolve(fakeResponse)); 63 | 64 | const colorBefore = getEnvironmentAwareColor(); 65 | await executeCommand(Commands.changeColorToFavorite); 66 | const colorAfter = getEnvironmentAwareColor(); 67 | stub.restore(); 68 | 69 | assert.ok(!isValidColorInput(colorAfter)); 70 | assert.ok(colorAfter === colorBefore); 71 | }); 72 | 73 | test('set to favorite color is noop when there are no favorites ', async () => { 74 | // set the color to peacock green to start 75 | await executeCommand(Commands.changeColorToPeacockGreen); 76 | 77 | // Stub the async quick pick to return a response 78 | const fakeResponse = ''; 79 | const stub = await sinon 80 | .stub(vscode.window, 'showQuickPick') 81 | .returns(Promise.resolve(fakeResponse)); 82 | 83 | // Save favorites 84 | const { values: favoriteColors } = getFavoriteColors(); 85 | originalValues.favoriteColors = favoriteColors; 86 | // Remove favorites 87 | await updateFavoriteColors([]); 88 | 89 | const colorBefore = getEnvironmentAwareColor(); 90 | await executeCommand(Commands.changeColorToFavorite); 91 | const colorAfter = getEnvironmentAwareColor(); 92 | stub.restore(); 93 | 94 | // Put back original favorites 95 | await updateFavoriteColors(originalValues.favoriteColors); 96 | 97 | assert.ok(colorBefore && colorAfter); 98 | assert.ok(isValidColorInput(colorAfter), `${colorAfter} is not a valid color`); 99 | assert.ok(colorBefore === colorAfter); 100 | }); 101 | }); 102 | -------------------------------------------------------------------------------- /src/test/suite/foreground.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as sinon from 'sinon'; 3 | import * as assert from 'assert'; 4 | import { IPeacockSettings, Commands, ColorSettings } from '../../models'; 5 | import { setupTestSuite, teardownTestSuite } from './lib/setup-teardown-test-suite'; 6 | import { isValidColorInput } from '../../color-library'; 7 | import { executeCommand } from './lib/constants'; 8 | import { 9 | getColorCustomizationConfig, 10 | getLightForegroundColorOrOverride, 11 | getDarkForegroundColorOrOverride, 12 | } from '../../configuration'; 13 | 14 | suite('Foreground color', () => { 15 | const originalValues = {} as IPeacockSettings; 16 | 17 | suiteSetup(async () => { 18 | await setupTestSuite(originalValues); 19 | }); 20 | 21 | suiteTeardown(() => teardownTestSuite(originalValues)); 22 | 23 | setup(async () => { 24 | await executeCommand(Commands.resetWorkspaceColors); 25 | }); 26 | 27 | test( 28 | 'is set to light foreground on black backgrounds', 29 | createForegroundTest('hsl (0, 0, 0)', getLightForegroundColorOrOverride()), 30 | ); 31 | 32 | test( 33 | 'is set to light foreground on dark backgrounds', 34 | createForegroundTest('hsl (0, 0, 25%)', getLightForegroundColorOrOverride()), 35 | ); 36 | 37 | test( 38 | 'is set to light foreground on less than 50% bright backgrounds', 39 | createForegroundTest('hsl (0, 0, 49%)', getLightForegroundColorOrOverride()), 40 | ); 41 | 42 | test( 43 | 'is set to dark foreground on greater than or equal to 50% bright backgrounds', 44 | createForegroundTest('hsl (0, 0, 50%)', getDarkForegroundColorOrOverride()), 45 | ); 46 | 47 | test( 48 | 'is set to dark foreground on light backgrounds', 49 | createForegroundTest('hsl (0, 0, 75%)', getDarkForegroundColorOrOverride()), 50 | ); 51 | 52 | test( 53 | 'is set to dark foreground on white backgrounds', 54 | createForegroundTest('hsl (0, 100%, 100%)', getDarkForegroundColorOrOverride()), 55 | ); 56 | }); 57 | 58 | function createForegroundTest(fakeResponse: string, expectedValue: string) { 59 | return async () => { 60 | // Stub the async input box to return a response 61 | const stub = await sinon 62 | .stub(vscode.window, 'showInputBox') 63 | .returns(Promise.resolve(fakeResponse)); 64 | 65 | // fire the command 66 | await executeCommand(Commands.enterColor); 67 | const config = getColorCustomizationConfig(); 68 | const value = config[ColorSettings.activityBar_foreground]; 69 | stub.restore(); 70 | 71 | assert.ok(isValidColorInput(value)); 72 | assert.equal(expectedValue, value); 73 | }; 74 | } 75 | -------------------------------------------------------------------------------- /src/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as Mocha from 'mocha'; 3 | import * as glob from 'glob'; 4 | import { createReport } from '../coverage'; 5 | 6 | export function run(): Promise { 7 | // Create the mocha test 8 | const mocha = new Mocha({ 9 | ui: 'tdd', 10 | //---------------------------------------- 11 | // Stuff from old test setup 12 | timeout: 7500, // longer timeout, in case 13 | // useColors: true, // colored output from test results 14 | //---------------------------------------- 15 | reporter: 'mocha-multi-reporters', 16 | reporterOptions: { 17 | reporterEnabled: 'spec, xunit', 18 | xunitReporterOptions: { 19 | output: path.join(__dirname, '..', '..', 'test-results.xml'), 20 | }, 21 | }, 22 | }); 23 | // mocha.useColors(true); 24 | 25 | const testsRoot = path.resolve(__dirname, '..'); 26 | 27 | return new Promise((c, e) => { 28 | glob('**/**.test.js', { cwd: testsRoot }, (err: any, files: any) => { 29 | if (err) { 30 | return e(err); 31 | } 32 | 33 | // Add files to the test suite 34 | files.forEach((f: any) => mocha.addFile(path.resolve(testsRoot, f))); 35 | 36 | try { 37 | // Run the mocha test 38 | mocha.run(failures => { 39 | if (failures > 0) { 40 | e(new Error(`${failures} tests failed.`)); 41 | } else { 42 | c(); 43 | } 44 | }); 45 | } catch (err) { 46 | e(err); 47 | } 48 | }); 49 | }).then(() => { 50 | // Tests have finished executing, check if we should generate a coverage report 51 | if (process.env['GENERATE_COVERAGE']) { 52 | createReport(); 53 | } 54 | }); 55 | } 56 | -------------------------------------------------------------------------------- /src/test/suite/lib/constants.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as sinon from 'sinon'; 3 | import { IPeacockAffectedElementSettings, IPeacockElementAdjustments } from '../../../models'; 4 | 5 | export const executeCommand = vscode.commands.executeCommand; 6 | 7 | export const noopElementAdjustments = { 8 | activityBar: 'none', 9 | statusBar: 'none', 10 | titleBar: 'none', 11 | } as IPeacockElementAdjustments; 12 | 13 | export const lightenActivityBarElementAdjustments = { 14 | activityBar: 'lighten', 15 | statusBar: 'none', 16 | titleBar: 'none', 17 | } as IPeacockElementAdjustments; 18 | 19 | export const allAffectedElements = { 20 | activityBar: true, 21 | statusBar: true, 22 | titleBar: true, 23 | } as IPeacockAffectedElementSettings; 24 | 25 | export const stubQuickPick = async (fakeResponse: string) => 26 | await sinon.stub(vscode.window, 'showQuickPick').returns(Promise.resolve(fakeResponse)); 27 | export const stubInputBox = async (fakeResponse: string) => 28 | await sinon.stub(vscode.window, 'showInputBox').returns(Promise.resolve(fakeResponse)); 29 | -------------------------------------------------------------------------------- /src/test/suite/lib/setup-teardown-test-suite.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IPeacockSettings, 3 | Commands, 4 | ForegroundColors, 5 | starterSetOfFavorites, 6 | getExtension, 7 | } from '../../../models'; 8 | import { 9 | getAffectedElements, 10 | getFavoriteColors, 11 | updateFavoriteColors, 12 | updateElementAdjustments, 13 | updateKeepForegroundColor, 14 | getKeepForegroundColor, 15 | updateSurpriseMeOnStartup, 16 | getDarkForegroundColor, 17 | getLightForegroundColor, 18 | updateDarkForegroundColor, 19 | updateLightForegroundColor, 20 | updateAffectedElements, 21 | updateSurpriseMeFromFavoritesOnly, 22 | getSurpriseMeFromFavoritesOnly, 23 | getShowColorInStatusBar, 24 | updateShowColorInStatusBar, 25 | getPeacockColor, 26 | getPeacockRemoteColor, 27 | updatePeacockColor, 28 | updatePeacockRemoteColor, 29 | getKeepBadgeColor, 30 | updateKeepBadgeColor, 31 | } from '../../../configuration'; 32 | 33 | import { noopElementAdjustments, executeCommand, allAffectedElements } from './constants'; 34 | 35 | export async function setupTest() { 36 | await executeCommand(Commands.resetWorkspaceColors); 37 | } 38 | 39 | export async function setupTestSuite( 40 | // extension: vscode.Extension | undefined, 41 | originalValues: IPeacockSettings, 42 | ) { 43 | const extension = getExtension(); 44 | 45 | // Save the original values 46 | originalValues.affectedElements = getAffectedElements(); 47 | originalValues.keepForegroundColor = getKeepForegroundColor(); 48 | const { values: favoriteColors } = getFavoriteColors(); 49 | originalValues.favoriteColors = favoriteColors; 50 | originalValues.darkForegroundColor = getDarkForegroundColor(); 51 | originalValues.lightForegroundColor = getLightForegroundColor(); 52 | originalValues.keepBadgeColor = getKeepBadgeColor(); 53 | originalValues.surpriseMeFromFavoritesOnly = getSurpriseMeFromFavoritesOnly(); 54 | originalValues.showColorInStatusBar = getShowColorInStatusBar(); 55 | originalValues.color = getPeacockColor(); 56 | originalValues.remoteColor = getPeacockRemoteColor(); 57 | 58 | // Set the test values 59 | await updateAffectedElements(allAffectedElements); 60 | await updateFavoriteColors(starterSetOfFavorites); 61 | await updateKeepForegroundColor(false); 62 | await updateSurpriseMeOnStartup(false); 63 | await updateElementAdjustments(noopElementAdjustments); 64 | await updateDarkForegroundColor(ForegroundColors.DarkForeground); 65 | await updateLightForegroundColor(ForegroundColors.LightForeground); 66 | await updateKeepBadgeColor(false); 67 | await updateSurpriseMeFromFavoritesOnly(false); 68 | await updateShowColorInStatusBar(true); 69 | await updatePeacockColor(undefined); 70 | await updatePeacockRemoteColor(undefined); 71 | return extension; 72 | } 73 | 74 | export async function teardownTestSuite(originalValues: IPeacockSettings) { 75 | await executeCommand(Commands.resetWorkspaceColors); 76 | 77 | // put back the original peacock user settings 78 | 79 | await updateAffectedElements(originalValues.affectedElements); 80 | await updateElementAdjustments(originalValues.elementAdjustments); 81 | await updateFavoriteColors(originalValues.favoriteColors); 82 | await updateKeepForegroundColor(originalValues.keepForegroundColor); 83 | await updateDarkForegroundColor(originalValues.darkForegroundColor); 84 | await updateLightForegroundColor(originalValues.lightForegroundColor); 85 | await updateKeepBadgeColor(originalValues.keepBadgeColor); 86 | await updateSurpriseMeFromFavoritesOnly(originalValues.surpriseMeFromFavoritesOnly); 87 | await updateSurpriseMeOnStartup(originalValues.surpriseMeOnStartup); 88 | await updateShowColorInStatusBar(originalValues.showColorInStatusBar); 89 | await updatePeacockColor(originalValues.color); 90 | await updatePeacockRemoteColor(originalValues.remoteColor); 91 | } 92 | -------------------------------------------------------------------------------- /src/test/suite/notification.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import { IPeacockSettings } from '../../models'; 3 | import { setupTestSuite, teardownTestSuite, setupTest } from './lib/setup-teardown-test-suite'; 4 | import { notify } from '../../notification'; 5 | import sinon = require('sinon'); 6 | import { Logger } from '../../logging'; 7 | 8 | suite('Notification Tests', () => { 9 | const originalValues = {} as IPeacockSettings; 10 | 11 | suiteSetup(async () => await setupTestSuite(originalValues)); 12 | suiteTeardown(async () => await teardownTestSuite(originalValues)); 13 | setup(async () => await setupTest()); 14 | 15 | test('Can fire notification without logging', () => { 16 | const loggerStub = sinon.stub(Logger, 'info'); 17 | notify('test message'); 18 | assert.ok(!loggerStub.called); 19 | loggerStub.restore(); 20 | }); 21 | 22 | test('Can fire notification with logging', () => { 23 | const loggerStub = sinon.stub(Logger, 'info'); 24 | notify('test message', true); 25 | assert.ok(loggerStub.called); 26 | loggerStub.restore(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /src/test/suite/reset.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import { IPeacockSettings, ElementNames, peacockGreen, azureBlue, Commands } from '../../models'; 3 | import { setupTestSuite, teardownTestSuite, setupTest } from './lib/setup-teardown-test-suite'; 4 | import { 5 | getOriginalColorsForAllElements, 6 | updatePeacockColorInUserSettings, 7 | updatePeacockColor, 8 | getPeacockColor, 9 | } from '../../configuration'; 10 | import { executeCommand } from './lib/constants'; 11 | 12 | suite('Reset Tests', () => { 13 | const originalValues = {} as IPeacockSettings; 14 | 15 | suiteSetup(async () => await setupTestSuite(originalValues)); 16 | suiteTeardown(async () => await teardownTestSuite(originalValues)); 17 | setup(async () => await setupTest()); 18 | 19 | suite('when resetting workspace colors', () => { 20 | test('when global color exists, workspace still has colors applied', async () => { 21 | await updatePeacockColorInUserSettings(peacockGreen); 22 | await updatePeacockColor(azureBlue); 23 | 24 | await executeCommand(Commands.resetWorkspaceColors); 25 | 26 | const color = getPeacockColor(); 27 | const appliedColors = getOriginalColorsForAllElements(); 28 | 29 | assert.equal(color, peacockGreen, 'Color should be equal to what is in the User Settings'); 30 | assert.equal( 31 | appliedColors[ElementNames.statusBar], 32 | peacockGreen, 33 | 'Applied color should be equal to what is in the User Settings', 34 | ); 35 | }); 36 | 37 | test('when global color does not exist, no colors should be in the workspace color customizations', async () => { 38 | await updatePeacockColorInUserSettings(undefined); 39 | await updatePeacockColor(azureBlue); 40 | 41 | await executeCommand(Commands.resetWorkspaceColors); 42 | 43 | const color = getPeacockColor(); 44 | const appliedColors = getOriginalColorsForAllElements(); 45 | 46 | assert.ok( 47 | !color, 48 | 'Color should be undefined since there are no peacock.color in any settings', 49 | ); 50 | assert.ok(!appliedColors[ElementNames.statusBar], 'No colors should be applied'); 51 | }); 52 | }); 53 | 54 | test('when removing all colors, no colors nor color customizations remain', async () => { 55 | await updatePeacockColorInUserSettings(peacockGreen); 56 | await updatePeacockColor(azureBlue); 57 | 58 | await executeCommand(Commands.removeAllColors); 59 | 60 | const color = getPeacockColor(); 61 | const appliedColors = getOriginalColorsForAllElements(); 62 | 63 | assert.ok(!color, 'Color should be undefined since there are no peacock.color in any settings'); 64 | assert.ok(!appliedColors[ElementNames.statusBar], 'No colors should be applied'); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /src/test/suite/save-favorite-color.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as assert from 'assert'; 3 | import * as sinon from 'sinon'; 4 | import { Commands, IPeacockSettings } from '../../models'; 5 | import { setupTestSuite, teardownTestSuite, setupTest } from './lib/setup-teardown-test-suite'; 6 | import { executeCommand, lightenActivityBarElementAdjustments } from './lib/constants'; 7 | import { 8 | getFavoriteColors, 9 | updateElementAdjustments, 10 | getEnvironmentAwareColor, 11 | } from '../../configuration'; 12 | 13 | const faveName = 'TEST FAVE NAME'; 14 | 15 | suite('Save favorite color', () => { 16 | const originalValues = {} as IPeacockSettings; 17 | suiteSetup(async () => await setupTestSuite(originalValues)); 18 | suiteTeardown(async () => await teardownTestSuite(originalValues)); 19 | setup(async () => await setupTest()); 20 | 21 | suiteSetup(async () => { 22 | await updateElementAdjustments(lightenActivityBarElementAdjustments); 23 | }); 24 | 25 | setup(async () => { 26 | await executeCommand(Commands.changeColorToPeacockGreen); 27 | }); 28 | 29 | test('with valid name successfully', async () => { 30 | // Stub the async input box to return a response 31 | const stub = await sinon.stub(vscode.window, 'showInputBox').returns(Promise.resolve(faveName)); 32 | 33 | await executeCommand(Commands.saveColorToFavorites); 34 | const { values: favoriteColors } = getFavoriteColors(); 35 | stub.restore(); 36 | 37 | assert.ok(favoriteColors.some(f => f.name === faveName)); 38 | }); 39 | 40 | test('with no name should not add the favorite', async () => { 41 | const emptyName = ''; 42 | // Stub the async input box to return a response 43 | const stub = await sinon 44 | .stub(vscode.window, 'showInputBox') 45 | .returns(Promise.resolve(emptyName)); 46 | 47 | const { values: favoriteColorsBefore } = getFavoriteColors(); 48 | await executeCommand(Commands.saveColorToFavorites); 49 | const { values: favoriteColorsAfter } = getFavoriteColors(); 50 | stub.restore(); 51 | 52 | assert.equal(favoriteColorsBefore.length, favoriteColorsAfter.length); 53 | assert.ok(!favoriteColorsAfter.some(f => f.name === emptyName)); 54 | }); 55 | 56 | test('current color does not change', async () => { 57 | // activity bar is lighter than the titlebar and statusbar 58 | // BUT the color should not change, as we should grab the color 59 | // before any adjustments are made 60 | 61 | const currentColor = getEnvironmentAwareColor(); 62 | 63 | // Stub the async input box to return a response 64 | const stub = await sinon.stub(vscode.window, 'showInputBox').returns(Promise.resolve(faveName)); 65 | 66 | await executeCommand(Commands.saveColorToFavorites); 67 | stub.restore(); 68 | 69 | const newCurrentColor = getEnvironmentAwareColor(); 70 | // console.log(`currentColor=${currentColor} and newCurrentColor = ${newCurrentColor}`); 71 | 72 | assert.equal(currentColor, newCurrentColor); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /src/test/suite/save-starter-favorite-colors.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import { IPeacockSettings, Commands, starterSetOfFavorites } from '../../models'; 3 | import { setupTestSuite, teardownTestSuite, setupTest } from './lib/setup-teardown-test-suite'; 4 | import { 5 | getFavoriteColors, 6 | writeRecommendedFavoriteColors, 7 | updateFavoriteColors, 8 | } from '../../configuration'; 9 | import { executeCommand, stubInputBox } from './lib/constants'; 10 | 11 | suite('Save starter favorite colors', () => { 12 | const originalValues = {} as IPeacockSettings; 13 | 14 | suiteSetup(async () => await setupTestSuite(originalValues)); 15 | suiteTeardown(async () => await teardownTestSuite(originalValues)); 16 | setup(async () => await setupTest()); 17 | 18 | suite('when adding recommended favorites', async () => { 19 | test('any removed ones are added back', async () => { 20 | const vueGreenName = 'Vue Green'; 21 | // Start with starterSetOfFavorites, minus Vue 22 | 23 | // Remove 1 starterFaves (Vue green) 24 | const starter = starterSetOfFavorites.filter(item => item.name !== 'Vue Green'); 25 | await updateFavoriteColors(starter); 26 | 27 | // Get the faves 28 | // we should not find Vue green 29 | const { values: favesWithoutVueGreen } = getFavoriteColors(); 30 | assert.ok(!favesWithoutVueGreen.find(item => item.name === vueGreenName)); 31 | 32 | // Now write the recommended favorites 33 | await executeCommand(Commands.addRecommendedFavorites); 34 | 35 | // Get the faves 36 | const { values: faves } = getFavoriteColors(); 37 | 38 | // we should have the 1 that was removed 39 | assert.ok(faves.find(item => item.name === vueGreenName)); 40 | }); 41 | 42 | test('any added ones are still there', async () => { 43 | // Start with starterSetOfFavorites 44 | await updateFavoriteColors(starterSetOfFavorites); 45 | 46 | // Add Night Blue #103362 to faves. 47 | const nightBlue = '#103362'; 48 | const nightBlueName = 'Night Blue'; 49 | 50 | // input the hex #103362 51 | const qiColorStub = await stubInputBox(nightBlue); 52 | await executeCommand(Commands.enterColor); 53 | qiColorStub.restore(); 54 | 55 | // enter the name "Night Blue" 56 | const qiNameStub = await stubInputBox(nightBlueName); 57 | await executeCommand(Commands.saveColorToFavorites); 58 | qiNameStub.restore(); 59 | 60 | // Now write the recommended favorites 61 | await executeCommand(Commands.addRecommendedFavorites); 62 | 63 | // Get the faves 64 | const { values: favoriteColorsAfter } = getFavoriteColors(); 65 | 66 | // re-added and the one new one, as well/ 67 | assert.ok(favoriteColorsAfter.find(item => item.name === nightBlueName)); 68 | }); 69 | }); 70 | 71 | test('general test', async () => { 72 | // started with: 73 | // starterSetOfFavorites; 74 | 75 | const { values: favoriteColorsBefore } = getFavoriteColors(); 76 | 77 | const vueGreen = favoriteColorsBefore.find(item => item.name === 'Vue Green') || { 78 | name: '', 79 | value: '', 80 | }; 81 | 82 | const azureBlue = favoriteColorsBefore.find(item => item.name === 'Azure Blue') || { 83 | name: '', 84 | value: '', 85 | }; 86 | 87 | const newRecs = { 88 | yodaGreen: { 89 | name: 'Yoda Green', // new key 90 | value: 'green', // new value 91 | }, 92 | azureBlue: { 93 | name: 'Azure Blue', // same key 94 | value: '#007fff', // same value 95 | }, 96 | vueGreen: { 97 | name: 'Vue Green', // same key 98 | value: 'lightgreen', // new value 99 | }, 100 | }; 101 | 102 | const testRecommended = [newRecs.vueGreen, newRecs.azureBlue, newRecs.yodaGreen]; 103 | 104 | // Write 3 to favorites 105 | await writeRecommendedFavoriteColors(testRecommended); 106 | const { values: favoriteColors2 } = getFavoriteColors(); 107 | 108 | // new unioned set should be one larger than before 109 | assert.ok(favoriteColors2.length === favoriteColorsBefore.length + 1); 110 | 111 | // 2 Should have same old value for existing favorite azure 112 | assert.ok( 113 | favoriteColors2.some( 114 | f => 115 | f.name === azureBlue.name && 116 | f.value === azureBlue.value && 117 | f.value === newRecs.azureBlue.value, 118 | ), 119 | ); 120 | // 2 Should have different value for existing favorite Vue Green 121 | console.log(vueGreen.name); 122 | console.log(vueGreen.value); 123 | console.log(newRecs.vueGreen.value); 124 | assert.ok( 125 | favoriteColors2.some( 126 | f => 127 | f.name === vueGreen.name && 128 | f.value !== vueGreen.value && 129 | f.value === newRecs.vueGreen.value, 130 | ), 131 | ); 132 | // 2 Should have new key and value pair (yoda/green) 133 | assert.ok( 134 | favoriteColors2.some( 135 | f => f.name === newRecs.yodaGreen.name && f.value === newRecs.yodaGreen.value, 136 | ), 137 | ); 138 | }); 139 | }); 140 | -------------------------------------------------------------------------------- /src/test/suite/status-bar.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as assert from 'assert'; 3 | import { IPeacockSettings, Commands, getExtension, peacockGreen } from '../../models'; 4 | import { setupTestSuite, teardownTestSuite, setupTest } from './lib/setup-teardown-test-suite'; 5 | import { getStatusBarItem } from '../../statusbar'; 6 | 7 | suite.skip('StatusBar Tests', () => { 8 | const originalValues = {} as IPeacockSettings; 9 | let extension: vscode.Extension; 10 | 11 | suiteSetup(async () => await setupTestSuite(originalValues)); 12 | suiteTeardown(async () => await teardownTestSuite(originalValues)); 13 | setup(async () => await setupTest()); 14 | 15 | suiteSetup(() => { 16 | extension = getExtension() as vscode.Extension; 17 | }); 18 | 19 | test('status bar', async () => { 20 | await extension.activate(); 21 | 22 | await vscode.commands.executeCommand(Commands.changeColorToPeacockGreen); 23 | 24 | // show the status bar 25 | // await vscode.workspace 26 | // .getConfiguration() 27 | // .update('peacock.showColorInStatusBar', true, vscode.ConfigurationTarget.Global); 28 | 29 | const s = getStatusBarItem(); 30 | 31 | // status bar should exist 32 | assert.ok(!!s); 33 | 34 | // status bar text should not be undefined 35 | 36 | assert.ok(s.text !== undefined); 37 | 38 | // statusbar text should have text 39 | assert.ok(s.text.length); 40 | 41 | // statusbar text should include current color 42 | assert.ok(s.text.includes(peacockGreen)); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /src/test/suite/surprise-me-on-startup.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as assert from 'assert'; 3 | import { 4 | Commands, 5 | IPeacockSettings, 6 | IPeacockAffectedElementSettings, 7 | IElementColors, 8 | ElementNames, 9 | } from '../../models'; 10 | import { setupTestSuite, teardownTestSuite, setupTest } from './lib/setup-teardown-test-suite'; 11 | import { executeCommand } from './lib/constants'; 12 | import { 13 | getOriginalColorsForAllElements, 14 | updateAffectedElements, 15 | updateSurpriseMeOnStartup, 16 | } from '../../configuration'; 17 | import { checkSurpriseMeOnStartupLogic } from '../../extension'; 18 | 19 | suite('Surprise me on startup', () => { 20 | const originalValues = {} as IPeacockSettings; 21 | 22 | suiteSetup(async () => await setupTestSuite(originalValues)); 23 | suiteTeardown(async () => await teardownTestSuite(originalValues)); 24 | setup(async () => await setupTest()); 25 | 26 | setup(async () => { 27 | await executeCommand(Commands.resetWorkspaceColors); 28 | await updateAffectedElements({ 29 | statusBar: true, 30 | activityBar: true, 31 | titleBar: true, 32 | } as IPeacockAffectedElementSettings); 33 | }); 34 | 35 | test('when not set has no effect if no customizations exist', async () => { 36 | await testColorsBeforeAndAfterInitialConfiguration(assert.equal); 37 | }); 38 | 39 | suite('when set', () => { 40 | setup(async () => { 41 | await updateSurpriseMeOnStartup(true); 42 | }); 43 | 44 | test('applies a random color if no color customizations exist', async () => { 45 | await testColorsBeforeAndAfterInitialConfiguration(assert.notEqual); 46 | }); 47 | 48 | test('has no effect when color customizations exist', async () => { 49 | await vscode.commands.executeCommand(Commands.changeColorToPeacockGreen); 50 | await testColorsBeforeAndAfterInitialConfiguration(assert.equal); 51 | }); 52 | 53 | teardown(async () => { 54 | await updateSurpriseMeOnStartup(false); 55 | }); 56 | }); 57 | 58 | async function testColorsBeforeAndAfterInitialConfiguration(assertEquality: EqualityAssertion) { 59 | const colors1: IElementColors = getOriginalColorsForAllElements(); 60 | await checkSurpriseMeOnStartupLogic(); 61 | const colors2: IElementColors = getOriginalColorsForAllElements(); 62 | assertEquality(colors1[ElementNames.activityBar], colors2[ElementNames.activityBar]); 63 | assertEquality(colors1[ElementNames.statusBar], colors2[ElementNames.statusBar]); 64 | assertEquality(colors1[ElementNames.titleBar], colors2[ElementNames.titleBar]); 65 | } 66 | 67 | interface EqualityAssertion { 68 | (actual: any, expected: any, message?: string | Error | undefined): void; 69 | } 70 | }); 71 | -------------------------------------------------------------------------------- /testworkspace/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "peacock.cacheBust": "827478", 4 | "peacock.color": "#f9e64f", 5 | "workbench.colorCustomizations": { 6 | "activityBar.activeBackground": "#fbed80", 7 | "activityBar.activeBorder": "#06b9a5", 8 | "activityBar.background": "#fbed80", 9 | "activityBar.foreground": "#15202b", 10 | "activityBar.inactiveForeground": "#15202b99", 11 | "activityBarBadge.background": "#06b9a5", 12 | "activityBarBadge.foreground": "#15202b", 13 | "sash.hoverBorder": "#fbed80", 14 | "statusBar.background": "#f9e64f", 15 | "statusBar.foreground": "#15202b", 16 | "statusBarItem.hoverBackground": "#f7df1e", 17 | "statusBarItem.remoteBackground": "#f9e64f", 18 | "statusBarItem.remoteForeground": "#15202b", 19 | "titleBar.activeBackground": "#f9e64f", 20 | "titleBar.activeForeground": "#15202b", 21 | "titleBar.inactiveBackground": "#f9e64f99", 22 | "titleBar.inactiveForeground": "#15202b99" 23 | }, 24 | "peacock.remoteColor": "" 25 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": "src", 11 | "strict": true /* enable all strict type-checking options */ 12 | /* Additional Checks */ 13 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 14 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 15 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | ".vscode-test" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /vsc-extension-quickstart.md: -------------------------------------------------------------------------------- 1 | # Welcome to your VS Code Extension 2 | 3 | ## What's in the folder 4 | 5 | - This folder contains all of the files necessary for your extension. 6 | - `package.json` - this is the manifest file in which you declare your extension and command. 7 | - The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesn’t yet need to load the plugin. 8 | - `src/extension.ts` - this is the main file where you will provide the implementation of your command. 9 | - The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`. 10 | - We pass the function containing the implementation of the command as the second parameter to `registerCommand`. 11 | 12 | ## Get up and running straight away 13 | 14 | - Press `F5` to open a new window with your extension loaded. 15 | - Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`. 16 | - Set breakpoints in your code inside `src/extension.ts` to debug your extension. 17 | - Find output from your extension in the debug console. 18 | 19 | ## Make changes 20 | 21 | - You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`. 22 | - You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes. 23 | 24 | ## Explore the API 25 | 26 | - You can open the full set of our API when you open the file `node_modules/vscode/vscode.d.ts`. 27 | 28 | ## Run tests 29 | 30 | - Open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Extension Tests`. 31 | - Press `F5` to run the tests in a new window with your extension loaded. 32 | - See the output of the test result in the debug console. 33 | - Make changes to `test/extension.test.ts` or create new test files inside the `test` folder. 34 | - By convention, the test runner will only consider files matching the name pattern `**.test.ts`. 35 | - You can create folders inside the `test` folder to structure your tests any way you want. 36 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | //@ts-check 2 | 3 | 'use strict'; 4 | 5 | const path = require('path'); 6 | 7 | /**@type {import('webpack').Configuration}*/ 8 | const baseConfig = { 9 | entry: './src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ 10 | devtool: 'source-map', 11 | externals: { 12 | vscode: 'commonjs vscode' // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/ 13 | }, 14 | resolve: { 15 | // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader 16 | extensions: ['.ts', '.js'] 17 | }, 18 | module: { 19 | rules: [ 20 | { 21 | test: /\.ts$/, 22 | exclude: /node_modules/, 23 | use: [ 24 | { 25 | loader: 'ts-loader' 26 | } 27 | ] 28 | } 29 | ] 30 | } 31 | }; 32 | 33 | const nodeConfig = { 34 | ... baseConfig, 35 | target: 'node', 36 | output: { 37 | // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ 38 | path: path.resolve(__dirname, 'dist'), 39 | filename: 'extension-node.js', 40 | libraryTarget: 'commonjs2', 41 | devtoolModuleFilenameTemplate: '../[resource-path]' 42 | } 43 | }; 44 | 45 | const webConfig = { 46 | ... baseConfig, 47 | target: 'webworker', 48 | output: { 49 | // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ 50 | path: path.resolve(__dirname, 'dist'), 51 | filename: 'extension-web.js', 52 | libraryTarget: 'commonjs2', 53 | devtoolModuleFilenameTemplate: '../[resource-path]' 54 | } 55 | }; 56 | 57 | module.exports = [nodeConfig, webConfig]; 58 | --------------------------------------------------------------------------------