├── .appcast.xml ├── .gitignore ├── LICENSE ├── README.md ├── assets └── icon.png ├── colors ├── ant-design.txt ├── atlassian-design.txt └── material-design.txt ├── package-lock.json ├── package.json ├── src ├── export.js ├── import.js ├── lib │ ├── aco-to-colors.js │ ├── act-to-colors.js │ ├── ase-to-colors.js │ ├── clr-to-colors.js │ ├── color.js │ ├── gpl-to-colors.js │ ├── is-json-string.js │ ├── is-zip.js │ ├── sketch-to-colors.js │ ├── sketchpalette-to-colors.js │ ├── sketchpreset-to-colors.js │ └── txt-to-colors.js └── manifest.json └── webpack.skpm.config.js /.appcast.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build artifacts 2 | import-colors.sketchplugin 3 | 4 | # npm 5 | node_modules 6 | .npm 7 | npm-debug.log 8 | 9 | # mac 10 | .DS_Store 11 | 12 | # WebStorm 13 | .idea 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ashung Hung 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Import Colors 2 | 3 | Import colors from swatches file to Sketch. **(For Sketch 53+)** 4 | 5 | ## Installation 6 | 7 | - [Download](https://github.com/Ashung/import-colors-sketch/releases/latest/download/import-colors.sketchplugin.zip) zip file, unzip and double click on the ".sketchplugin" file. 8 | - Search "import colors" from [Sketch Runner](http://sketchrunner.com/). 9 | 10 | ## Features 11 | 12 | ### V1.0.x (for Sketch 53 ~ 68.x) 13 | 14 | - Import Colors to Document / Global 15 | - Import Colors as Library 16 | - Export Document / Global Colors to .txt or .clr file 17 | - Convert Colors to .txt or .clr File 18 | - Reset Global Colors 19 | 20 | ### V2.0.x (for Sketch 69+) 21 | 22 | - Import Colors to Color Variables 23 | - Import Colors as Library 24 | - Import Colors and Update Color Variables 25 | - Convert Color Variables to .txt or .clr File 26 | - Export Color Variables to .txt or .clr file 27 | 28 | ## Supported Formats 29 | 30 | | Name | Extension | Support Application | 31 | | -------------------------- | ---------------- | ----------------------------------- | 32 | | Apple Color Picker Palette | `.clr` | macOS Color Picker 1 | 33 | | Adobe Color Swatch | `.aco` | Photoshop 2 | 34 | | Adobe Color Table | `.act` | Photoshop 2 | 35 | | Adobe Swatch Exchange | `.ase` | Photoshop, Illustrator 2 | 36 | | GIMP Palette | `.gpl` | GIMP, Inkscape | 37 | | Sketch Palette | `.sketchpalette` | Sketch (old version) | 38 | | Sketch Preset | `.sketchpreset` | Sketch 3 | 39 | | Sketch Document | `.sketch` | Sketch 3 | 40 | | Text File | `.txt`, `.text` | Text Editor 4 | 41 | 42 | 1. In Sketch run "View" - "Show Colors", select palette tab, then click the gear icon, to open .clr files. 43 | 2. The color save in CMYK, LAB, Grayscale model from .aco or .ase file, maybe have different hex value between Sketch and Photoshop. 44 | 3. Only import document colors. 45 | 4. Save colors in each lines, like `red: #ff0000`, color support hex, CSS color name, 8-digit hex, rgb, rgba, hsl, hsla. 46 | 47 | ## License 48 | 49 | MIT 50 | 51 | ## Donate 52 | 53 | [Buy me a coffee](https://www.buymeacoffee.com/ashung) or donate [$2.00](https://www.paypal.me/ashung/2) [$5.00](https://www.paypal.me/ashung/5) [$10.00](https://www.paypal.me/ashung/10) via PayPal. 54 | 55 | ----- 56 | 57 | > **NOTE** 58 | > _This plugin was created using `skpm`. For a detailed explanation on how things work, checkout the [skpm Readme](https://github.com/skpm/skpm/blob/master/README.md)._ 59 | -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashung/import-colors-sketch/733f1bb8519cb01139c742d6cb0d4b26ee40c656/assets/icon.png -------------------------------------------------------------------------------- /colors/ant-design.txt: -------------------------------------------------------------------------------- 1 | red-1: #fff1f0 2 | red-2: #ffccc7 3 | red-3: #ffa39e 4 | red-4: #ff7875 5 | red-5: #ff4d4f 6 | red-6: #f5222d 7 | red-7: #cf1322 8 | red-8: #a8071a 9 | red-9: #820014 10 | red-10: #5c0011 11 | volcano-1: #fff2e8 12 | volcano-2: #ffd8bf 13 | volcano-3: #ffbb96 14 | volcano-4: #ff9c6e 15 | volcano-5: #ff7a45 16 | volcano-6: #fa541c 17 | volcano-7: #d4380d 18 | volcano-8: #ad2102 19 | volcano-9: #871400 20 | volcano-10: #610b00 21 | orange-1: #fff7e6 22 | orange-2: #ffe7ba 23 | orange-3: #ffd591 24 | orange-4: #ffc069 25 | orange-5: #ffa940 26 | orange-6: #fa8c16 27 | orange-7: #d46b08 28 | orange-8: #ad4e00 29 | orange-9: #873800 30 | orange-10: #612500 31 | gold-1: #fffbe6 32 | gold-2: #fff1b8 33 | gold-3: #ffe58f 34 | gold-4: #ffd666 35 | gold-5: #ffc53d 36 | gold-6: #faad14 37 | gold-7: #d48806 38 | gold-8: #ad6800 39 | gold-9: #874d00 40 | gold-10: #613400 41 | yellow-1: #feffe6 42 | yellow-2: #ffffb8 43 | yellow-3: #fffb8f 44 | yellow-4: #fff566 45 | yellow-5: #ffec3d 46 | yellow-6: #fadb14 47 | yellow-7: #d4b106 48 | yellow-8: #ad8b00 49 | yellow-9: #876800 50 | yellow-10: #614700 51 | lime-1: #fcffe6 52 | lime-2: #f4ffb8 53 | lime-3: #eaff8f 54 | lime-4: #d3f261 55 | lime-5: #bae637 56 | lime-6: #a0d911 57 | lime-7: #7cb305 58 | lime-8: #5b8c00 59 | lime-9: #3f6600 60 | lime-10: #254000 61 | green-1: #f6ffed 62 | green-2: #d9f7be 63 | green-3: #b7eb8f 64 | green-4: #95de64 65 | green-5: #73d13d 66 | green-6: #52c41a 67 | green-7: #389e0d 68 | green-8: #237804 69 | green-9: #135200 70 | green-10: #092b00 71 | cyan-1: #e6fffb 72 | cyan-2: #b5f5ec 73 | cyan-3: #87e8de 74 | cyan-4: #5cdbd3 75 | cyan-5: #36cfc9 76 | cyan-6: #13c2c2 77 | cyan-7: #08979c 78 | cyan-8: #006d75 79 | cyan-9: #00474f 80 | cyan-10: #002329 81 | blue-1: #e6f7ff 82 | blue-2: #bae7ff 83 | blue-3: #91d5ff 84 | blue-4: #69c0ff 85 | blue-5: #40a9ff 86 | blue-6: #1890ff 87 | blue-7: #096dd9 88 | blue-8: #0050b3 89 | blue-9: #003a8c 90 | blue-10: #002766 91 | geekblue-1: #f0f5ff 92 | geekblue-2: #d6e4ff 93 | geekblue-3: #adc6ff 94 | geekblue-4: #85a5ff 95 | geekblue-5: #597ef7 96 | geekblue-6: #2f54eb 97 | geekblue-7: #1d39c4 98 | geekblue-8: #10239e 99 | geekblue-9: #061178 100 | geekblue-10: #030852 101 | purple-1: #f9f0ff 102 | purple-2: #efdbff 103 | purple-3: #d3adf7 104 | purple-4: #b37feb 105 | purple-5: #9254de 106 | purple-6: #722ed1 107 | purple-7: #531dab 108 | purple-8: #391085 109 | purple-9: #22075e 110 | purple-10: #120338 111 | magenta-1: #fff0f6 112 | magenta-2: #ffd6e7 113 | magenta-3: #ffadd2 114 | magenta-4: #ff85c0 115 | magenta-5: #f759ab 116 | magenta-6: #eb2f96 117 | magenta-7: #c41d7f 118 | magenta-8: #9e1068 119 | magenta-9: #780650 120 | magenta-10: #520339 -------------------------------------------------------------------------------- /colors/atlassian-design.txt: -------------------------------------------------------------------------------- 1 | R500 - Dragon's blood: #BF2600 2 | R400 - Red dirt: #DE350B 3 | R300 - Poppy surprise: #FF5630 4 | R200 - Salmon sashimi: #FF7452 5 | R100 - Alexandria: #FF8F73 6 | R75 - Bondi sunburn: #FFBDAD 7 | R50 - Rosie: #FFEBE6 8 | Y500 - Debrito: #FF8B00 9 | Y400 - Cheezy blasters: #FF991F 10 | Y300 - Golden state: #FFAB00 11 | Y200 - Pub mix: #FFC400 12 | Y100 - Cowbell: #FFE380 13 | Y75 - Dandelion whisper: #FFF0B3 14 | Y50 - James blonde: #FFFAE6 15 | G500 - Keen green: #006644 16 | G400 - Slime: #00875A 17 | G300 - Fine pine: #36B37E 18 | G200 - Green tea: #57D9A3 19 | G100 - Cloverleaf: #79F2C0 20 | G75 - Mintie: #ABF5D1 21 | G50 - The smell: #E3FCEF 22 | T500 - Shabby chic: #008DA6 23 | T400 - Prom dress: #00A3BF 24 | T300 - Tamarama: #00B8D9 25 | T200 - Mermaid net: #00C7E6 26 | T100 - Hairy fairy: #79E2F2 27 | T75 - Arctic chill: #B3F5FF 28 | T50 - Gram's sofa: #E6FCFF 29 | B500 - Chore coat: #0747A6 30 | B400 - Pacific bridge: #0052CC 31 | B300 - Sodium explosion: #0065FF 32 | B200 - Coogee: #2684FF 33 | B100 - Arvo breeze: #4C9AFF 34 | B75 - Schwag: #B3D4FF 35 | B50 - Pixie dust: #DEEBFF 36 | P500 - Prince: #403294 37 | P400 - Snozzberry: #5243AA 38 | P300 - Da' juice: #6554C0 39 | P200 - Pastelli: #8777D9 40 | P100 - Herky jerky: #998DD9 41 | P75 - Phantom mist: #C0B6F2 42 | P50 - Lavender secret: #EAE6FF 43 | N900 - Slate: #091E42 44 | N800 - Squid ink: #172B4D 45 | N700 - Snorlax: #253858 46 | N600 - Pet rock: #344563 47 | N500 - McFanning: #42526E 48 | N400 - Concrete jungle: #505F79 49 | N300 - Clooney: #5E6C84 50 | N200 - Bling bling: #6B778C 51 | N100 - Humboldt fog: #7A869A 52 | N90 - Meredith: #8993A4 53 | N80 - Spooky ghost: #97A0AF 54 | N70 - Blanche: #A5ADBA 55 | N60 - Sentinel: #B3BAC5 56 | N50 - Karl: #C1C7D0 57 | N40 - Jolly fun time: #DFE1E6 58 | N30 - Northeast snow: #EBECF0 59 | N20 - Gram's hair: #F4F5F7 60 | N10 - Wash me: #FAFBFC 61 | N0 - Doctor: #FFFFFF -------------------------------------------------------------------------------- /colors/material-design.txt: -------------------------------------------------------------------------------- 1 | Red 50: #FFEBEE 2 | Red 100: #FFCDD2 3 | Red 200: #EF9A9A 4 | Red 300: #E57373 5 | Red 400: #EF5350 6 | Red 500: #F44336 7 | Red 600: #E53935 8 | Red 700: #D32F2F 9 | Red 800: #C62828 10 | Red 900: #B71C1C 11 | Red A100: #FF8A80 12 | Red A200: #FF5252 13 | Red A400: #FF1744 14 | Red A700: #D50000 15 | Pink 50: #FCE4EC 16 | Pink 100: #F8BBD0 17 | Pink 200: #F48FB1 18 | Pink 300: #F06292 19 | Pink 400: #EC407A 20 | Pink 500: #E91E63 21 | Pink 600: #D81B60 22 | Pink 700: #C2185B 23 | Pink 800: #AD1457 24 | Pink 900: #880E4F 25 | Pink A100: #FF80AB 26 | Pink A200: #FF4081 27 | Pink A400: #F50057 28 | Pink A700: #C51162 29 | Purple 50: #F3E5F5 30 | Purple 100: #E1BEE7 31 | Purple 200: #CE93D8 32 | Purple 300: #BA68C8 33 | Purple 400: #AB47BC 34 | Purple 500: #9C27B0 35 | Purple 600: #8E24AA 36 | Purple 700: #7B1FA2 37 | Purple 800: #6A1B9A 38 | Purple 900: #4A148C 39 | Purple A100: #EA80FC 40 | Purple A200: #E040FB 41 | Purple A400: #D500F9 42 | Purple A700: #AA00FF 43 | Deep purple 50: #EDE7F6 44 | Deep purple 100: #D1C4E9 45 | Deep purple 200: #B39DDB 46 | Deep purple 300: #9575CD 47 | Deep purple 400: #7E57C2 48 | Deep purple 500: #673AB7 49 | Deep purple 600: #5E35B1 50 | Deep purple 700: #512DA8 51 | Deep purple 800: #4527A0 52 | Deep purple 900: #311B92 53 | Deep purple A100: #B388FF 54 | Deep purple A200: #7C4DFF 55 | Deep purple A400: #651FFF 56 | Deep purple A700: #6200EA 57 | Indigo 50: #E8EAF6 58 | Indigo 100: #C5CAE9 59 | Indigo 200: #9FA8DA 60 | Indigo 300: #7986CB 61 | Indigo 400: #5C6BC0 62 | Indigo 500: #3F51B5 63 | Indigo 600: #3949AB 64 | Indigo 700: #303F9F 65 | Indigo 800: #283593 66 | Indigo 900: #1A237E 67 | Indigo A100: #8C9EFF 68 | Indigo A200: #536DFE 69 | Indigo A400: #3D5AFE 70 | Indigo A700: #304FFE 71 | Blue 50: #E3F2FD 72 | Blue 100: #BBDEFB 73 | Blue 200: #90CAF9 74 | Blue 300: #64B5F6 75 | Blue 400: #42A5F5 76 | Blue 500: #2196F3 77 | Blue 600: #1E88E5 78 | Blue 700: #1976D2 79 | Blue 800: #1565C0 80 | Blue 900: #0D47A1 81 | Blue A100: #82B1FF 82 | Blue A200: #448AFF 83 | Blue A400: #2979FF 84 | Blue A700: #2962FF 85 | Light Blue 50: #E1F5FE 86 | Light Blue 100: #B3E5FC 87 | Light Blue 200: #81D4FA 88 | Light Blue 300: #4FC3F7 89 | Light Blue 400: #29B6F6 90 | Light Blue 500: #03A9F4 91 | Light Blue 600: #039BE5 92 | Light Blue 700: #0288D1 93 | Light Blue 800: #0277BD 94 | Light Blue 900: #01579B 95 | Light Blue A100: #80D8FF 96 | Light Blue A200: #40C4FF 97 | Light Blue A400: #00B0FF 98 | Light Blue A700: #0091EA 99 | Cyan 50: #E0F7FA 100 | Cyan 100: #B2EBF2 101 | Cyan 200: #80DEEA 102 | Cyan 300: #4DD0E1 103 | Cyan 400: #26C6DA 104 | Cyan 500: #00BCD4 105 | Cyan 600: #00ACC1 106 | Cyan 700: #0097A7 107 | Cyan 800: #00838F 108 | Cyan 900: #006064 109 | Cyan A100: #84FFFF 110 | Cyan A200: #18FFFF 111 | Cyan A400: #00E5FF 112 | Cyan A700: #00B8D4 113 | Teal 50: #E0F2F1 114 | Teal 100: #B2DFDB 115 | Teal 200: #80CBC4 116 | Teal 300: #4DB6AC 117 | Teal 400: #26A69A 118 | Teal 500: #009688 119 | Teal 600: #00897B 120 | Teal 700: #00796B 121 | Teal 800: #00695C 122 | Teal 900: #004D40 123 | Teal A100: #A7FFEB 124 | Teal A200: #64FFDA 125 | Teal A400: #1DE9B6 126 | Teal A700: #00BFA5 127 | Green 50: #E8F5E9 128 | Green 100: #C8E6C9 129 | Green 200: #A5D6A7 130 | Green 300: #81C784 131 | Green 400: #66BB6A 132 | Green 500: #4CAF50 133 | Green 600: #43A047 134 | Green 700: #388E3C 135 | Green 800: #2E7D32 136 | Green 900: #1B5E20 137 | Green A100: #B9F6CA 138 | Green A200: #69F0AE 139 | Green A400: #00E676 140 | Green A700: #00C853 141 | Light Green 50: #F1F8E9 142 | Light Green 100: #DCEDC8 143 | Light Green 200: #C5E1A5 144 | Light Green 300: #AED581 145 | Light Green 400: #9CCC65 146 | Light Green 500: #8BC34A 147 | Light Green 600: #7CB342 148 | Light Green 700: #689F38 149 | Light Green 800: #558B2F 150 | Light Green 900: #33691E 151 | Light Green A100: #CCFF90 152 | Light Green A200: #B2FF59 153 | Light Green A400: #76FF03 154 | Light Green A700: #64DD17 155 | Lime 50: #F9FBE7 156 | Lime 100: #F0F4C3 157 | Lime 200: #E6EE9C 158 | Lime 300: #DCE775 159 | Lime 400: #D4E157 160 | Lime 500: #CDDC39 161 | Lime 600: #C0CA33 162 | Lime 700: #AFB42B 163 | Lime 800: #9E9D24 164 | Lime 900: #827717 165 | Lime A100: #F4FF81 166 | Lime A200: #EEFF41 167 | Lime A400: #C6FF00 168 | Lime A700: #AEEA00 169 | Yellow 50: #FFFDE7 170 | Yellow 100: #FFF9C4 171 | Yellow 200: #FFF59D 172 | Yellow 300: #FFF176 173 | Yellow 400: #FFEE58 174 | Yellow 500: #FFEB3B 175 | Yellow 600: #FDD835 176 | Yellow 700: #FBC02D 177 | Yellow 800: #F9A825 178 | Yellow 900: #F57F17 179 | Yellow A100: #FFFF8D 180 | Yellow A200: #FFFF00 181 | Yellow A400: #FFEA00 182 | Yellow A700: #FFD600 183 | Amber 50: #FFF8E1 184 | Amber 100: #FFECB3 185 | Amber 200: #FFE082 186 | Amber 300: #FFD54F 187 | Amber 400: #FFCA28 188 | Amber 500: #FFC107 189 | Amber 600: #FFB300 190 | Amber 700: #FFA000 191 | Amber 800: #FF8F00 192 | Amber 900: #FF6F00 193 | Amber A100: #FFE57F 194 | Amber A200: #FFD740 195 | Amber A400: #FFC400 196 | Amber A700: #FFAB00 197 | Orange 50: #FFF3E0 198 | Orange 100: #FFE0B2 199 | Orange 200: #FFCC80 200 | Orange 300: #FFB74D 201 | Orange 400: #FFA726 202 | Orange 500: #FF9800 203 | Orange 600: #FB8C00 204 | Orange 700: #F57C00 205 | Orange 800: #EF6C00 206 | Orange 900: #E65100 207 | Orange A100: #FFD180 208 | Orange A200: #FFAB40 209 | Orange A400: #FF9100 210 | Orange A700: #FF6D00 211 | Deep Orange 50: #FBE9E7 212 | Deep Orange 100: #FFCCBC 213 | Deep Orange 200: #FFAB91 214 | Deep Orange 300: #FF8A65 215 | Deep Orange 400: #FF7043 216 | Deep Orange 500: #FF5722 217 | Deep Orange 600: #F4511E 218 | Deep Orange 700: #E64A19 219 | Deep Orange 800: #D84315 220 | Deep Orange 900: #BF360C 221 | Deep Orange A100: #FF9E80 222 | Deep Orange A200: #FF6E40 223 | Deep Orange A400: #FF3D00 224 | Deep Orange A700: #DD2C00 225 | Brown 50: #EFEBE9 226 | Brown 100: #D7CCC8 227 | Brown 200: #BCAAA4 228 | Brown 300: #A1887F 229 | Brown 400: #8D6E63 230 | Brown 500: #795548 231 | Brown 600: #6D4C41 232 | Brown 700: #5D4037 233 | Brown 800: #4E342E 234 | Brown 900: #3E2723 235 | Gray 50: #FAFAFA 236 | Gray 100: #F5F5F5 237 | Gray 200: #EEEEEE 238 | Gray 300: #E0E0E0 239 | Gray 400: #BDBDBD 240 | Gray 500: #9E9E9E 241 | Gray 600: #757575 242 | Gray 700: #616161 243 | Gray 800: #424242 244 | Gray 900: #212121 245 | Blue Gray 50: #ECEFF1 246 | Blue Gray 100: #CFD8DC 247 | Blue Gray 200: #B0BEC5 248 | Blue Gray 300: #90A4AE 249 | Blue Gray 400: #78909C 250 | Blue Gray 500: #607D8B 251 | Blue Gray 600: #546E7A 252 | Blue Gray 700: #455A64 253 | Blue Gray 800: #37474F 254 | Blue Gray 900: #263238 255 | Black: #000000 256 | White: #FFFFFF -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "import-colors", 3 | "version": "2.0.3", 4 | "description": "Import colors from swatches file to Sketch. (For Sketch 69+)", 5 | "engines": { 6 | "sketch": ">=3.0" 7 | }, 8 | "skpm": { 9 | "name": "import-colors", 10 | "manifest": "src/manifest.json", 11 | "main": "import-colors.sketchplugin", 12 | "assets": [ 13 | "assets/**/*" 14 | ] 15 | }, 16 | "scripts": { 17 | "build": "export NODE_OPTIONS=--openssl-legacy-provider && skpm-build", 18 | "watch": "export NODE_OPTIONS=--openssl-legacy-provider && skpm-build --watch", 19 | "start": "export NODE_OPTIONS=--openssl-legacy-provider && skpm-build --watch --run", 20 | "postinstall": "export NODE_OPTIONS=--openssl-legacy-provider && npm run build && skpm-link" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/Ashung/import-colors-sketch.git" 25 | }, 26 | "author": "Ashung ", 27 | "license": "MIT", 28 | "dependencies": { 29 | "@skpm/dialog": "^0.2.6", 30 | "@skpm/fs": "^0.2.6" 31 | }, 32 | "devDependencies": { 33 | "@skpm/builder": "^0.7.7" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/export.js: -------------------------------------------------------------------------------- 1 | import { UI } from 'sketch'; 2 | import sketch from 'sketch/dom'; 3 | import { Swatch } from 'sketch/dom'; 4 | import dialog from '@skpm/dialog'; 5 | import { writeFileSync } from '@skpm/fs'; 6 | import color from './lib/color'; 7 | import { toArray } from 'util'; 8 | import { extname } from 'path'; 9 | 10 | export default function(context) { 11 | 12 | const document = sketch.getSelectedDocument(); 13 | const identifier = String(__command.identifier()); 14 | 15 | let colors; 16 | dialog.showMessageBox( 17 | { 18 | type: 'none', 19 | buttons: ['OK', 'Cancel'], 20 | message: 'Export colors', 21 | checkboxLabel: 'Include color variables from library.', 22 | checkboxChecked: false 23 | }, 24 | ({ response, checkboxChecked }) => { 25 | if (response === 0) { 26 | if (checkboxChecked) { 27 | colors = toArray(document._getMSDocumentData().allSwatches()).map(swatch => { 28 | return Swatch.fromNative(swatch); 29 | }); 30 | } else { 31 | colors = document.swatches; 32 | } 33 | } else { 34 | return; 35 | } 36 | } 37 | ); 38 | 39 | if (colors.length === 0) { 40 | UI.message('Document have no color variables.'); 41 | return; 42 | } 43 | 44 | let filter; 45 | if (identifier === 'export-color-variables-to-clr-file') { 46 | filter = { name: 'Apple Color Picker Palette', extensions: [ 'clr' ] } 47 | } 48 | if (identifier === 'export-color-variables-to-txt-file') { 49 | filter = { name: 'Text File', extensions: [ 'txt', 'text' ] } 50 | } 51 | 52 | dialog.showSaveDialog( 53 | { 54 | filters: [filter] 55 | }, 56 | (filePath) => { 57 | if (identifier === 'export-color-variables-to-clr-file') { 58 | if (extname(filePath) === '') { 59 | filePath += '.clr'; 60 | } 61 | let colorList = color.colorListFromArray(colors); 62 | colorList.writeToFile(filePath); 63 | } 64 | if (identifier === 'export-color-variables-to-txt-file') { 65 | if (extname(filePath) === '') { 66 | filePath += '.txt'; 67 | } 68 | let keyCount = {}; 69 | let text = color.toTextContent(colors, keyCount); 70 | writeFileSync(filePath, text); 71 | } 72 | UI.message('Colors save to "' + filePath + '".'); 73 | } 74 | ); 75 | 76 | } -------------------------------------------------------------------------------- /src/import.js: -------------------------------------------------------------------------------- 1 | import { UI } from 'sketch'; 2 | import { Document, Artboard, ShapePath, Rectangle, Library, Style, Swatch } from 'sketch/dom'; 3 | import sketch from 'sketch/dom'; 4 | import { extname, basename } from 'path'; 5 | import os from 'os'; 6 | import dialog from '@skpm/dialog'; 7 | import { existsSync, mkdirSync, readdirSync, unlinkSync, writeFileSync } from '@skpm/fs'; 8 | import color from './lib/color'; 9 | import clr2colors from './lib/clr-to-colors'; 10 | import gpl2colors from './lib/gpl-to-colors'; 11 | import aco2colors from './lib/aco-to-colors'; 12 | import act2colors from './lib/act-to-colors'; 13 | import ase2colors from './lib/ase-to-colors'; 14 | import sketchpreset2colors from './lib/sketchpreset-to-colors'; 15 | import sketchpalette2colors from './lib/sketchpalette-to-colors'; 16 | import sketch2colors from './lib/sketch-to-colors'; 17 | import txt2colors from './lib/txt-to-colors'; 18 | 19 | export default function(context) { 20 | 21 | const identifier = String(__command.identifier()); 22 | 23 | dialog.showOpenDialog({ 24 | filters: [ 25 | { name: 'Apple Color Picker Palette', extensions: [ 'clr' ] }, 26 | { name: 'Adobe Color Swatch', extensions: [ 'aco' ] }, 27 | { name: 'Adobe Color Table', extensions: [ 'act' ] }, 28 | { name: 'Adobe Swatch Exchange', extensions: [ 'ase' ] }, 29 | { name: 'GIMP Palette', extensions: [ 'gpl' ] }, 30 | { name: 'Sketch', extensions: [ 'sketchpreset', 'sketchpalette', 'sketch' ] }, 31 | { name: 'Text File', extensions: [ 'txt', 'text' ] } 32 | ], 33 | properties: [ 'openFile' ] 34 | }, (filePaths) => { 35 | 36 | let colors; 37 | let filePath = filePaths[0]; 38 | let fileName = basename(filePath); 39 | let fileType = extname(filePath).toLowerCase(); 40 | if (fileType === '.clr') { 41 | colors = clr2colors(filePath); 42 | } else if (fileType === '.aco') { 43 | colors = aco2colors(filePath); 44 | } else if (fileType === '.act') { 45 | colors = act2colors(filePath); 46 | } else if (fileType === '.ase') { 47 | colors = ase2colors(filePath); 48 | } else if (fileType === '.gpl') { 49 | colors = gpl2colors(filePath); 50 | } else if (fileType === '.sketchpreset') { 51 | colors = sketchpreset2colors(filePath); 52 | } else if (fileType === '.sketchpalette') { 53 | colors = sketchpalette2colors(filePath); 54 | } else if (fileType === '.sketch') { 55 | if (sketch.getSelectedDocument().path === filePath) { 56 | UI.message('The sketch file is current document.'); 57 | return; 58 | } 59 | colors = sketch2colors(filePath); 60 | } else if (fileType === '.txt' || fileType === '.text') { 61 | colors = txt2colors(filePath); 62 | } else { 63 | UI.message('Unknown file format.'); 64 | } 65 | 66 | if (colors === undefined) { 67 | return; 68 | } 69 | 70 | if (Array.isArray(colors) && colors.length === 0) { 71 | UI.message('No colors.'); 72 | return; 73 | } 74 | 75 | if (identifier === 'import-colors-to-color-variables') { 76 | 77 | let document = sketch.getSelectedDocument(); 78 | let swatches = document.swatches; 79 | if (swatches.length === 0) { 80 | dialog.showMessageBox( 81 | { 82 | type: 'none', 83 | buttons: ['OK', 'Cancel'], 84 | message: 'Import colors from "' + fileName + '"?', 85 | checkboxLabel: 'Auto add numbers in front of Color Variables.', 86 | checkboxChecked: true 87 | }, 88 | ({ response, checkboxChecked }) => { 89 | if (response === 0) { 90 | addColorVariables(checkboxChecked); 91 | } else { 92 | return; 93 | } 94 | } 95 | ); 96 | } else { 97 | dialog.showMessageBox( 98 | { 99 | type: 'none', 100 | buttons: ['OK', 'Cancel', 'Append'], 101 | message: 'Remove all existing color variables, before import colors from "' + fileName + '"?', 102 | checkboxLabel: 'Auto add numbers in front of Color Variables.', 103 | checkboxChecked: true 104 | }, 105 | ({ response, checkboxChecked }) => { 106 | if (response === 0) { 107 | removeAllColorVariables(); 108 | addColorVariables(checkboxChecked); 109 | } else if (response === 1) { 110 | return; 111 | } else if (response === 2) { 112 | addColorVariables(checkboxChecked); 113 | } 114 | } 115 | ); 116 | } 117 | 118 | function removeAllColorVariables() { 119 | document.swatches = []; 120 | } 121 | 122 | function addColorVariables(addNumber) { 123 | colors.forEach((item, index) => { 124 | let name = item.name; 125 | if (addNumber) { 126 | name = item.name.replace(/\/([^\/]*)$/, `/${index + 1}. $1`); 127 | } 128 | const swatch = Swatch.from({ 129 | name, 130 | color: item.color 131 | }); 132 | swatches.push(swatch); 133 | }); 134 | UI.message(`Import ${colors.length} color variable${colors.length > 1 ? 's' : ''} to document.`); 135 | } 136 | 137 | } 138 | 139 | else if (identifier === 'convert-colors-to-clr-file') { 140 | dialog.showSaveDialog( 141 | { 142 | filters: [ 143 | { name: 'Apple Color Picker Palette', extensions: [ 'clr' ] } 144 | ] 145 | }, 146 | (filePath) => { 147 | let colorList = color.colorListFromArray(colors); 148 | colorList.writeToFile(filePath); 149 | UI.message('Colors have convert to .clr file.'); 150 | } 151 | ); 152 | } 153 | 154 | else if (identifier === 'convert-colors-to-txt-file') { 155 | dialog.showSaveDialog( 156 | { 157 | filters: [ 158 | { name: 'Text File', extensions: [ 'txt', 'text' ] } 159 | ] 160 | }, 161 | (filePath) => { 162 | let keyCount = {}; 163 | let text = color.toTextContent(colors, keyCount); 164 | writeFileSync(filePath, text); 165 | UI.message('Colors have convert to .txt file.'); 166 | } 167 | ); 168 | } 169 | 170 | else if (identifier === 'import-colors-as-library') { 171 | 172 | dialog.showMessageBox( 173 | { 174 | type: 'none', 175 | buttons: ['OK', 'Cancel'], 176 | message: 'Import colors as library from "' + fileName + '"?', 177 | checkboxLabel: 'Auto add numbers in front of Color Variables.', 178 | checkboxChecked: true 179 | }, 180 | ({ response, checkboxChecked }) => { 181 | if (response === 1) { 182 | return; 183 | } else { 184 | 185 | let libraryFolder = os.homedir() + '/Library/Application Support/com.bohemiancoding.sketch3/Plugins/import-colors-libraries/'; 186 | let libraryPath = libraryFolder + fileName.replace(/\.\w+$/i, '.sketch'); 187 | 188 | if (!existsSync(libraryFolder)) { 189 | mkdirSync(libraryFolder); 190 | } 191 | 192 | let msDocument = MSDocument.alloc().init(); 193 | let document = Document.fromNative(msDocument); 194 | 195 | // Page 196 | document.pages[0].name = 'Library Preview'; 197 | 198 | // Add artboard 199 | let artboard = new Artboard({ 200 | name: 'Library Preview', 201 | parent: document.pages[0], 202 | frame: new Rectangle(0, 0, 400, 400) 203 | }); 204 | 205 | colors.forEach((item, index) => { 206 | 207 | // Add colors 208 | const colorName = (checkboxChecked ? `${index + 1}. ` : '') + item.name; 209 | const swatch = Swatch.from({ 210 | name: colorName, 211 | color: item.color 212 | }); 213 | document.swatches.push(swatch); 214 | 215 | // Add layers 4 x 4 216 | if (index < 16) { 217 | let x = (index % 4) * 90 + 40; 218 | let y = Math.floor(index / 4) * 90 + 40; 219 | const layer = new ShapePath({ 220 | name: colorName, 221 | parent: artboard, 222 | shapeType: ShapePath.ShapeType.Oval, 223 | frame: new Rectangle(x, y, 50, 50), 224 | style: { 225 | fills: [ 226 | { 227 | color: swatch.referencingColor 228 | } 229 | ], 230 | borders: [ 231 | { 232 | color: '#0000001A', 233 | fillType: Style.FillType.Color, 234 | position: Style.BorderPosition.Inside 235 | } 236 | ] 237 | } 238 | }); 239 | } 240 | 241 | }); 242 | 243 | document.save(libraryPath, error => { 244 | if (error) { 245 | UI.message('Error: ' + error.message); 246 | } else { 247 | Library.getLibraryForDocumentAtPath(libraryPath); 248 | 249 | // Remove library files that remove from Libraries Preferences 250 | let allLibraries = sketch.getLibraries().map(item => { 251 | return String(item.sketchObject.locationOnDisk().path()); 252 | }); 253 | let colorLibraryFiles = readdirSync(libraryFolder).filter(item => { 254 | return extname(item) === '.sketch'; 255 | }).map(item => { 256 | return libraryFolder + item; 257 | }); 258 | colorLibraryFiles.forEach(item => { 259 | if (!allLibraries.includes(item)) { 260 | unlinkSync(item); 261 | } 262 | }); 263 | 264 | UI.message('Colors have imported as a library.'); 265 | } 266 | }); 267 | 268 | } 269 | } 270 | ); 271 | 272 | } 273 | 274 | else if (identifier === 'import-colors-and-update-color-variables') { 275 | let document = sketch.getSelectedDocument(); 276 | let swatches = document.swatches; 277 | let names = swatches.map(item => item.name); 278 | let countNew = 0; 279 | let countUpdate = 0; 280 | colors.forEach(item => { 281 | if (names.includes(item.name)) { 282 | let mscolor = color.mscolorWithHex(item.color); 283 | let swatch = swatches.find(_item => _item.name === item.name); 284 | swatch.sketchObject.updateWithColor(mscolor); 285 | let swatchContainer = document._getMSDocumentData().sharedSwatches(); 286 | swatchContainer.updateReferencesToSwatch(swatch.sketchObject); 287 | countUpdate ++; 288 | } else { 289 | swatches.push(Swatch.from({ 290 | name: item.name, 291 | color: item.color 292 | })); 293 | countNew ++; 294 | } 295 | UI.message(`Add: ${countNew}, Update: ${countUpdate}.`); 296 | }); 297 | } 298 | }); 299 | } -------------------------------------------------------------------------------- /src/lib/aco-to-colors.js: -------------------------------------------------------------------------------- 1 | import { UI } from 'sketch'; 2 | import { Buffer } from 'buffer'; 3 | import { readFileSync } from '@skpm/fs'; 4 | import color from './color'; 5 | 6 | /** 7 | * Convert Adobe color swatch (ACO) file to Array [{name, color}] 8 | * File format specification: 9 | * https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1055819 10 | * http://www.nomodes.com/aco.html 11 | * @param {String} filePath 12 | * @returns {Array} [ {name, color} ] 13 | */ 14 | export default function(filePath) { 15 | 16 | let colorContents = readFileSync(filePath); 17 | let colorBuffer = Buffer.from(colorContents); 18 | 19 | if (colorBuffer.length < 4) { 20 | UI.message('Error: Not a Adobe color swatch (ACO) file.'); 21 | return; 22 | } 23 | 24 | let version = colorBuffer.slice(0, 2).readUInt16BE(0); 25 | let count = colorBuffer.slice(2, 4).readUInt16BE(0); 26 | 27 | let colors = []; 28 | 29 | // version 1 30 | let i; 31 | if (version === 1 && (colorBuffer.length - 4) / 10 === count) { 32 | i = 4; 33 | while (i < colorBuffer.length) { 34 | let colorSpace = colorBuffer.slice(i, i + 2).readUInt16BE(0); 35 | let w = colorBuffer.slice(i + 2, i + 4).readUInt16BE(0); 36 | let x = colorBuffer.slice(i + 4, i + 6).readUInt16BE(0); 37 | let y = colorBuffer.slice(i + 6, i + 8).readUInt16BE(0); 38 | let z = colorBuffer.slice(i + 8, i + 10).readUInt16BE(0); 39 | let nscolor = color.colorFromAco(colorSpace, w, x, y, z); 40 | let hexValue = color.toHexValue(nscolor); 41 | colors.push({ 42 | name: null, 43 | color: hexValue 44 | }); 45 | i += 10; 46 | } 47 | } 48 | 49 | // version 2 50 | if ( 51 | (version === 2) || 52 | ( 53 | version === 1 && 54 | colorBuffer.length > count * 10 + 8 && 55 | colorBuffer.slice(4 + count * 10, 6 + count * 10).readUInt16BE(0) === 2 && 56 | colorBuffer.slice(6 + count * 10, 8 + count * 10).readUInt16BE(0) === count 57 | ) 58 | ) { 59 | i = 4 + count * 10 + 4; 60 | if (version === 2) { 61 | i = 4; 62 | } 63 | while (i < colorBuffer.length) { 64 | let colorSpace = colorBuffer.slice(i, i + 2).readUInt16BE(0); 65 | let w = colorBuffer.slice(i + 2, i + 4).readUInt16BE(0); 66 | let x = colorBuffer.slice(i + 4, i + 6).readUInt16BE(0); 67 | let y = colorBuffer.slice(i + 6, i + 8).readUInt16BE(0); 68 | let z = colorBuffer.slice(i + 8, i + 10).readUInt16BE(0); 69 | let colorName = ''; 70 | let nameLength = colorBuffer.slice(i + 12, i + 14).readUInt16BE(0); 71 | for (let j = 0; j < nameLength * 2 - 2; j += 2) { 72 | colorName += String.fromCodePoint(colorBuffer.slice(i + 14 + j, i + 16 + j).readUInt16BE(0)); 73 | } 74 | let nscolor = color.colorFromAco(colorSpace, w, x, y, z); 75 | let hexValue = color.toHexValue(nscolor); 76 | colors.push({ 77 | name: colorName, 78 | color: hexValue 79 | }); 80 | i += 14 + nameLength * 2; 81 | } 82 | } 83 | 84 | return colors; 85 | } -------------------------------------------------------------------------------- /src/lib/act-to-colors.js: -------------------------------------------------------------------------------- 1 | import { UI } from 'sketch'; 2 | import { Buffer } from 'buffer'; 3 | import { readFileSync } from '@skpm/fs'; 4 | import color from './color'; 5 | 6 | /** 7 | * Convert Adobe Color Table (ACT) file to Array [{ name, color }] 8 | * File format specification: 9 | * https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1070626 10 | * @param {string} filePath 11 | * @returns {Array} [ {name, color} ] 12 | */ 13 | export default function(filePath) { 14 | let colorContents = readFileSync(filePath); 15 | let colorBuffer = Buffer.from(colorContents); 16 | 17 | // should be 768 or 772 bytes long 18 | if (colorBuffer.length !== 768 && colorBuffer.length !== 772) { 19 | UI.message('Error: Not a valid Adobe Color Table (ACT) file.'); 20 | return; 21 | } 22 | 23 | let numColors = 255; 24 | 25 | // If file is 772 bytes long, there are 4 additional bytes remaining 26 | if (colorBuffer.length === 772) { 27 | // Two bytes for the number of colors to use. 28 | numColors = colorBuffer.slice(-4).readInt16BE(); 29 | } 30 | 31 | let colors = []; 32 | 33 | let i = 0; 34 | 35 | while (i < numColors * 3) { 36 | let r = colorBuffer.slice(i, i + 1).readUInt8(0); 37 | let g = colorBuffer.slice(i + 1, i + 2).readUInt8(0); 38 | let b = colorBuffer.slice(i + 2, i + 3).readUInt8(0); 39 | 40 | const nscolor = color.colorWithRGBA(r, g, b, 1); 41 | const hexValue = color.toHexValue(nscolor); 42 | 43 | colors.push({ 44 | name: hexValue, 45 | color: hexValue, 46 | }); 47 | 48 | i += 3; 49 | } 50 | 51 | return colors; 52 | } 53 | -------------------------------------------------------------------------------- /src/lib/ase-to-colors.js: -------------------------------------------------------------------------------- 1 | import { UI } from 'sketch'; 2 | import { Buffer } from 'buffer'; 3 | import { readFileSync } from '@skpm/fs'; 4 | import color from './color'; 5 | 6 | /** 7 | * Convert Adobe Swatch Exchange (ASE) file to Array [{name, color}] 8 | * File format specification: 9 | * http://www.selapa.net/swatches/colors/fileformats.php#adobe_ase 10 | * @param {String} filePath 11 | * @returns {Array} [ {name, color} ] 12 | */ 13 | export default function(filePath) { 14 | 15 | let colorContents = readFileSync(filePath); 16 | let colorBuffer = Buffer.from(colorContents); 17 | let signature = colorBuffer.toString('utf-8', 0, 4); 18 | let versionMajor = colorBuffer.slice(4, 6).readInt16BE(0); 19 | let versionMin = colorBuffer.slice(6, 8).readInt16BE(0); 20 | let count = colorBuffer.slice(8, 12).readInt32BE(0); 21 | 22 | if (colorBuffer.length > 12 && signature !== 'ASEF' && versionMajor !== 1 && versionMin !== 0) { 23 | UI.message('Error: Not Adobe Swatch Exchange (ASE) file.'); 24 | return; 25 | } 26 | 27 | let colors = []; 28 | 29 | let i = 12; 30 | while (i < colorBuffer.length) { 31 | 32 | let blockLength; 33 | let blockType = colorBuffer.slice(i, i + 2).readInt16BE(0).toString(16); 34 | i += 2; 35 | 36 | // Ignore group start c001, end c002 37 | if (blockType === 'c001') { 38 | blockLength = colorBuffer.slice(i, i + 4).readInt32BE(0); 39 | i += blockLength; 40 | } 41 | if (blockType === 'c002') { 42 | i += 2; 43 | } 44 | 45 | // Color entry, start 0001 46 | if (blockType === '1') { 47 | blockLength = colorBuffer.slice(i, i + 4).readInt32BE(0); 48 | let nameLength = colorBuffer.slice(i + 4, i + 6).readUInt16BE(0); 49 | let colorName = ''; 50 | let nscolor; 51 | for (let j = 0; j < nameLength * 2 - 2; j += 2) { 52 | colorName += String.fromCodePoint(colorBuffer.slice(i + 6 + j, i + 8 + j).readInt16BE(0)); 53 | } 54 | let _i = i + 6 + nameLength * 2; 55 | let colorModel = colorBuffer.slice(_i, _i + 4).toString('utf-8', 0, 4); 56 | _i += 4; 57 | if (colorModel === 'RGB ') { 58 | let r = colorBuffer.slice(_i, _i + 4).readFloatBE(0); 59 | _i += 4; 60 | let g = colorBuffer.slice(_i, _i + 4).readFloatBE(0); 61 | _i += 4; 62 | let b = colorBuffer.slice(_i, _i + 4).readFloatBE(0); 63 | nscolor = color.colorWithRGBA(r * 255, g * 255, b * 255, 1.0); 64 | } else if (colorModel === 'CMYK') { 65 | let c = colorBuffer.slice(_i, _i + 4).readFloatBE(0); 66 | _i += 4; 67 | let m = colorBuffer.slice(_i, _i + 4).readFloatBE(0); 68 | _i += 4; 69 | let y = colorBuffer.slice(_i, _i + 4).readFloatBE(0); 70 | _i += 4; 71 | let k = colorBuffer.slice(_i, _i + 4).readFloatBE(0); 72 | nscolor = color.colorWithCMYKA(c * 100, m * 100, y * 100, k * 100, 1.0); 73 | } else if (colorModel === 'LAB ') { 74 | let l = colorBuffer.slice(_i, _i + 4).readFloatBE(0); 75 | _i += 4; 76 | let a = colorBuffer.slice(_i, _i + 4).readFloatBE(0); 77 | _i += 4; 78 | let b = colorBuffer.slice(_i, _i + 4).readFloatBE(0); 79 | nscolor = color.colorWithLABA(l * 100, a * 100, b * 100, 1.0); 80 | } else if (colorModel === 'Gray') { 81 | let g = colorBuffer.slice(_i, _i + 4).readFloatBE(0); 82 | nscolor = color.colorWithGA((1 - g) * 100, 1.0); 83 | } 84 | 85 | let hexValue = color.toHexValue(nscolor); 86 | colors.push({ 87 | name: colorName, 88 | color: hexValue 89 | }); 90 | 91 | i += blockLength; 92 | } 93 | } 94 | 95 | return colors; 96 | } -------------------------------------------------------------------------------- /src/lib/clr-to-colors.js: -------------------------------------------------------------------------------- 1 | import color from './color'; 2 | 3 | /** 4 | * Read Apple CLR file, return a Array [{name, color}] 5 | * @param {String} filePath 6 | * @returns {Array} [ {name, color} ] 7 | */ 8 | export default function(filePath) { 9 | let colorList = NSColorList.alloc().initWithName_fromFile(null, filePath); 10 | return color.toArray(colorList); 11 | } 12 | -------------------------------------------------------------------------------- /src/lib/color.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | /** 4 | * @param {String} hexValue #[0-9A-F]{3,8} 5 | * @returns {NSColor} NSColor 6 | */ 7 | colorWithHex (hexValue) { 8 | return MSImmutableColor.colorWithSVGString(hexValue).NSColorWithColorSpace(nil); 9 | }, 10 | 11 | /** 12 | * @param {String} hexValue #[0-9A-F]{3,8} 13 | * @returns {MSColor} MSColor 14 | */ 15 | mscolorWithHex (hexValue) { 16 | return MSImmutableColor.colorWithSVGString(hexValue).newMutableCounterpart(); 17 | }, 18 | 19 | /** 20 | * @param {Number} r 0..255 21 | * @param {Number} g 0..255 22 | * @param {Number} b 0..255 23 | * @param {Number} a 0..1 24 | * @returns {NSColor} NSColor 25 | */ 26 | colorWithRGBA (r, g, b, a) { 27 | return NSColor.colorWithRed_green_blue_alpha(r / 255, g / 255, b / 255, a); 28 | }, 29 | 30 | /** 31 | * @param {Number} h 0..360 32 | * @param {Number} s 0..100 33 | * @param {Number} l 0..100 34 | * @param {Number} a 0..1 35 | * @returns {NSColor} NSColor 36 | */ 37 | colorWithHSLA (h, s, l, a) { 38 | h = h / 360; 39 | s = s / 100; 40 | l = l / 100; 41 | let r, g, b; 42 | if (s == 0) { 43 | r = g = b = l; // achromatic 44 | } else { 45 | function hue2rgb(p, q, t){ 46 | if(t < 0) t += 1; 47 | if(t > 1) t -= 1; 48 | if(t < 1 / 6) return p + (q - p) * 6 * t; 49 | if(t < 1 / 2) return q; 50 | if(t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; 51 | return p; 52 | } 53 | let q = l < 0.5 ? l * (1 + s) : l + s - l * s; 54 | let p = 2 * l - q; 55 | r = hue2rgb(p, q, h + 1 / 3); 56 | g = hue2rgb(p, q, h); 57 | b = hue2rgb(p, q, h - 1 / 3); 58 | } 59 | return NSColor.colorWithRed_green_blue_alpha(r, g, b, a); 60 | }, 61 | 62 | /** 63 | * @param {Number} h 0..360 64 | * @param {Number} s 0..100 65 | * @param {Number} b 0..100 66 | * @param {Number} a 0..1 67 | * @returns {NSColor} NSColor 68 | */ 69 | colorWithHSBA (h, s, b, a) { 70 | return NSColor.colorWithHue_saturation_brightness_alpha(h / 360, s / 100, b / 100, a); 71 | }, 72 | 73 | /** 74 | * @param {String} name CSS color name 75 | * @returns {NSColor} NSColor 76 | */ 77 | colorWithName (name) { 78 | let colors = { 79 | 'aliceblue': [240, 248, 255], 80 | 'antiquewhite': [250, 235, 215], 81 | 'aqua': [0, 255, 255], 82 | 'aquamarine': [127, 255, 212], 83 | 'azure': [240, 255, 255], 84 | 'beige': [245, 245, 220], 85 | 'bisque': [255, 228, 196], 86 | 'black': [0, 0, 0], 87 | 'blanchedalmond': [255, 235, 205], 88 | 'blue': [0, 0, 255], 89 | 'blueviolet': [138, 43, 226], 90 | 'brown': [165, 42, 42], 91 | 'burlywood': [222, 184, 135], 92 | 'cadetblue': [95, 158, 160], 93 | 'chartreuse': [127, 255, 0], 94 | 'chocolate': [210, 105, 30], 95 | 'coral': [255, 127, 80], 96 | 'cornflowerblue': [100, 149, 237], 97 | 'cornsilk': [255, 248, 220], 98 | 'crimson': [220, 20, 60], 99 | 'cyan': [0, 255, 255], 100 | 'darkblue': [0, 0, 139], 101 | 'darkcyan': [0, 139, 139], 102 | 'darkgoldenrod': [184, 134, 11], 103 | 'darkgray': [169, 169, 169], 104 | 'darkgreen': [0, 100, 0], 105 | 'darkgrey': [169, 169, 169], 106 | 'darkkhaki': [189, 183, 107], 107 | 'darkmagenta': [139, 0, 139], 108 | 'darkolivegreen': [85, 107, 47], 109 | 'darkorange': [255, 140, 0], 110 | 'darkorchid': [153, 50, 204], 111 | 'darkred': [139, 0, 0], 112 | 'darksalmon': [233, 150, 122], 113 | 'darkseagreen': [143, 188, 143], 114 | 'darkslateblue': [72, 61, 139], 115 | 'darkslategray': [47, 79, 79], 116 | 'darkslategrey': [47, 79, 79], 117 | 'darkturquoise': [0, 206, 209], 118 | 'darkviolet': [148, 0, 211], 119 | 'deeppink': [255, 20, 147], 120 | 'deepskyblue': [0, 191, 255], 121 | 'dimgray': [105, 105, 105], 122 | 'dimgrey': [105, 105, 105], 123 | 'dodgerblue': [30, 144, 255], 124 | 'firebrick': [178, 34, 34], 125 | 'floralwhite': [255, 250, 240], 126 | 'forestgreen': [34, 139, 34], 127 | 'fuchsia': [255, 0, 255], 128 | 'gainsboro': [220, 220, 220], 129 | 'ghostwhite': [248, 248, 255], 130 | 'gold': [255, 215, 0], 131 | 'goldenrod': [218, 165, 32], 132 | 'gray': [128, 128, 128], 133 | 'green': [0, 128, 0], 134 | 'greenyellow': [173, 255, 47], 135 | 'grey': [128, 128, 128], 136 | 'honeydew': [240, 255, 240], 137 | 'hotpink': [255, 105, 180], 138 | 'indianred': [205, 92, 92], 139 | 'indigo': [75, 0, 130], 140 | 'ivory': [255, 255, 240], 141 | 'khaki': [240, 230, 140], 142 | 'lavender': [230, 230, 250], 143 | 'lavenderblush': [255, 240, 245], 144 | 'lawngreen': [124, 252, 0], 145 | 'lemonchiffon': [255, 250, 205], 146 | 'lightblue': [173, 216, 230], 147 | 'lightcoral': [240, 128, 128], 148 | 'lightcyan': [224, 255, 255], 149 | 'lightgoldenrodyellow': [250, 250, 210], 150 | 'lightgray': [211, 211, 211], 151 | 'lightgreen': [144, 238, 144], 152 | 'lightgrey': [211, 211, 211], 153 | 'lightpink': [255, 182, 193], 154 | 'lightsalmon': [255, 160, 122], 155 | 'lightseagreen': [32, 178, 170], 156 | 'lightskyblue': [135, 206, 250], 157 | 'lightslategray': [119, 136, 153], 158 | 'lightslategrey': [119, 136, 153], 159 | 'lightsteelblue': [176, 196, 222], 160 | 'lightyellow': [255, 255, 224], 161 | 'lime': [0, 255, 0], 162 | 'limegreen': [50, 205, 50], 163 | 'linen': [250, 240, 230], 164 | 'magenta': [255, 0, 255], 165 | 'maroon': [128, 0, 0], 166 | 'mediumaquamarine': [102, 205, 170], 167 | 'mediumblue': [0, 0, 205], 168 | 'mediumorchid': [186, 85, 211], 169 | 'mediumpurple': [147, 112, 219], 170 | 'mediumseagreen': [60, 179, 113], 171 | 'mediumslateblue': [123, 104, 238], 172 | 'mediumspringgreen': [0, 250, 154], 173 | 'mediumturquoise': [72, 209, 204], 174 | 'mediumvioletred': [199, 21, 133], 175 | 'midnightblue': [25, 25, 112], 176 | 'mintcream': [245, 255, 250], 177 | 'mistyrose': [255, 228, 225], 178 | 'moccasin': [255, 228, 181], 179 | 'navajowhite': [255, 222, 173], 180 | 'navy': [0, 0, 128], 181 | 'oldlace': [253, 245, 230], 182 | 'olive': [128, 128, 0], 183 | 'olivedrab': [107, 142, 35], 184 | 'orange': [255, 165, 0], 185 | 'orangered': [255, 69, 0], 186 | 'orchid': [218, 112, 214], 187 | 'palegoldenrod': [238, 232, 170], 188 | 'palegreen': [152, 251, 152], 189 | 'paleturquoise': [175, 238, 238], 190 | 'palevioletred': [219, 112, 147], 191 | 'papayawhip': [255, 239, 213], 192 | 'peachpuff': [255, 218, 185], 193 | 'peru': [205, 133, 63], 194 | 'pink': [255, 192, 203], 195 | 'plum': [221, 160, 221], 196 | 'powderblue': [176, 224, 230], 197 | 'purple': [128, 0, 128], 198 | 'rebeccapurple': [102, 51, 153], 199 | 'red': [255, 0, 0], 200 | 'rosybrown': [188, 143, 143], 201 | 'royalblue': [65, 105, 225], 202 | 'saddlebrown': [139, 69, 19], 203 | 'salmon': [250, 128, 114], 204 | 'sandybrown': [244, 164, 96], 205 | 'seagreen': [46, 139, 87], 206 | 'seashell': [255, 245, 238], 207 | 'sienna': [160, 82, 45], 208 | 'silver': [192, 192, 192], 209 | 'skyblue': [135, 206, 235], 210 | 'slateblue': [106, 90, 205], 211 | 'slategray': [112, 128, 144], 212 | 'slategrey': [112, 128, 144], 213 | 'snow': [255, 250, 250], 214 | 'springgreen': [0, 255, 127], 215 | 'steelblue': [70, 130, 180], 216 | 'tan': [210, 180, 140], 217 | 'teal': [0, 128, 128], 218 | 'thistle': [216, 191, 216], 219 | 'tomato': [255, 99, 71], 220 | 'turquoise': [64, 224, 208], 221 | 'violet': [238, 130, 238], 222 | 'wheat': [245, 222, 179], 223 | 'white': [255, 255, 255], 224 | 'whitesmoke': [245, 245, 245], 225 | 'yellow': [255, 255, 0], 226 | 'yellowgreen': [154, 205, 50] 227 | }; 228 | if (colors[name.toLowerCase()]) { 229 | let r = colors[name][0] / 255; 230 | let g = colors[name][1] / 255; 231 | let b = colors[name][2] / 255; 232 | return NSColor.colorWithRed_green_blue_alpha(r, g, b, 1); 233 | } else { 234 | return null; 235 | } 236 | }, 237 | 238 | /** 239 | * @param {Number} c 0..100 240 | * @param {Number} m 0..100 241 | * @param {Number} y 0..100 242 | * @param {Number} k 0..100 243 | * @param {Number} a 0..1 244 | * @returns {NSColor} NSColor 245 | */ 246 | colorWithCMYKA (c, m, y, k, a) { 247 | return NSColor.colorWithDeviceCyan_magenta_yellow_black_alpha(c / 100, m / 100, y / 100, k / 100, a); 248 | }, 249 | 250 | /** 251 | * http://www.easyrgb.com/en/math.php 252 | * @param {Number} l 0..100 253 | * @param {Number} a -128..127 254 | * @param {Number} b -128..127 255 | * @param {Number} alpha 0..1 256 | * @returns {NSColor} NSColor 257 | */ 258 | colorWithLABA (l, a, b, alpha) { 259 | // CIE lab to CIE XYZ 260 | let y = (l + 16) / 116; 261 | let x = a / 500 + y; 262 | let z = y - b / 200; 263 | 264 | y = y * y * y > 0.008856 ? y * y * y : (y - 16 / 116) / 7.787; 265 | x = x * x * x > 0.008856 ? x * x * x : (x - 16 / 116) / 7.787; 266 | z = z * z * z > 0.008856 ? z * z * z : (z - 16 / 116) / 7.787; 267 | 268 | // D65 269 | x = x * 95.047; 270 | y = y * 100; 271 | z = z * 108.883; 272 | 273 | // CIE XYZ to sRGB 274 | x = x / 100; 275 | y = y / 100; 276 | z = z / 100; 277 | 278 | let R = x * 3.2406 + y * -1.5372 + z * -0.4986; 279 | let G = x * -0.9689 + y * 1.8758 + z * 0.0415; 280 | let B = x * 0.0557 + y * -0.2040 + z * 1.0570; 281 | 282 | R = R > 0.0031308 ? 1.055 * Math.pow(R, 1 / 2.4) - 0.055 : 12.92 * R; 283 | G = G > 0.0031308 ? 1.055 * Math.pow(G, 1 / 2.4) - 0.055 : 12.92 * G; 284 | B = B > 0.0031308 ? 1.055 * Math.pow(B, 1 / 2.4) - 0.055 : 12.92 * B; 285 | 286 | R = Math.min(Math.max(0, R), 1); 287 | G = Math.min(Math.max(0, G), 1); 288 | b = Math.min(Math.max(0, B), 1); 289 | 290 | return NSColor.colorWithRed_green_blue_alpha(R, G, B, alpha); 291 | }, 292 | 293 | /** 294 | * @param {Number} g grayscale 0..100 white..black 295 | * @param {Number} a 0..1 296 | * @returns {NSColor} NSColor 297 | */ 298 | colorWithGA (g, a) { 299 | return NSColor.colorWithWhite_alpha(1 - g / 100, a); 300 | }, 301 | 302 | /** 303 | * http://www.nomodes.com/aco.html 304 | * @param {Number} colorSpace 0: RGB, 1: HSB, 2: CMYK, 7: LAB, 8: Grayscale, 9, Wide CMYK 305 | * @param {Number} w 306 | * @param {Number} x 307 | * @param {Number} y 308 | * @param {Number} z 309 | * @returns {NSColor} NSColor 310 | */ 311 | colorFromAco (colorSpace, w, x, y, z) { 312 | if (colorSpace === 0) { 313 | // w: 0..65535, x: 0..65535, y: 0..65535, z: 0 314 | return this.colorWithRGBA(w / 65535 * 255, x / 65535 * 255, y / 65535 * 255, 1.0); 315 | } else if (colorSpace === 1) { 316 | // w: 0..65535, x: 0..65535, y: 0..65535, z: 0 317 | return this.colorWithHSBA(w / 65535 * 360, x / 65535 * 100, y / 65535 * 100, 1.0); 318 | } else if (colorSpace === 2) { 319 | // w: 0..65535, x: 0..65535, y: 0..65535, z: 0..65535 320 | return this.colorWithCMYKA(100 - w / 65535 * 100, 100 - x / 65535 * 100, 100 - y / 65535 * 100, 100 - z / 65535 * 100, 1.0); 321 | } else if (colorSpace === 7) { 322 | // w: 0..10000, x: -12800..12700, y: -12800..12700, z: 0 323 | if (x > 12700) { 324 | x = x - 65535; 325 | } 326 | if (y > 12700) { 327 | y = y - 65535; 328 | } 329 | return this.colorWithLABA(w / 100, x / 100, y / 100, 1.0); 330 | 331 | } else if (colorSpace === 8) { 332 | // w: 0..10000, x: 0, y: 0, z: 0 333 | return this.colorWithGA(w / 10000 * 100, 1.0); 334 | } else if (colorSpace === 9) { 335 | // w: 0..10000, x: 0..10000, y: 0..10000, z: 0..10000 336 | return this.colorWithCMYKA(w / 10000 * 100, x / 10000 * 100, y / 10000 * 100, z / 10000 * 100, 1.0); 337 | } else { 338 | return null; 339 | } 340 | }, 341 | 342 | /** 343 | * @param {NSColor} nscolor 344 | * @param {String} key 345 | * @param {NSColorList} colorList 346 | * @param {Object} keyCount 347 | */ 348 | addColorToList (nscolor, key, colorList, keyCount) { 349 | if (!isNaN(keyCount[key])) { 350 | keyCount[key] ++; 351 | } else { 352 | keyCount[key] = 1; 353 | } 354 | if (keyCount[key] === 1) { 355 | colorList.setColor_forKey(nscolor, key); 356 | } else { 357 | colorList.setColor_forKey(nscolor, key + ' ' + keyCount[key]); 358 | } 359 | }, 360 | 361 | /** 362 | * @param {Array} colorsArray 363 | * @returns {NSColorList} NSColorList 364 | */ 365 | colorListFromArray (colorsArray) { 366 | let colorList = NSColorList.alloc().initWithName(null); 367 | let keyCount = {}; 368 | colorsArray.forEach(item => { 369 | let nscolor = this.colorWithHex(item.color); 370 | let colorName = item.name || item.color; 371 | this.addColorToList(nscolor, colorName, colorList, keyCount); 372 | }); 373 | return colorList; 374 | }, 375 | 376 | /** 377 | * @param {NSColor} nscolor 378 | * @returns {String} [0-9A-F]{6|8} 379 | */ 380 | toHexValue (nscolor) { 381 | let color; 382 | if (String(nscolor.class()) === 'MSColor') { 383 | color = nscolor; 384 | } else { 385 | color = MSColor.colorWithNSColor(nscolor); 386 | } 387 | if (color.alpha() === 1) { 388 | return '#' + String(color.immutableModelObject().hexValue()); 389 | } else { 390 | return '#' + String(color.immutableModelObject().hexValue()) + this.floatToHex(color.alpha()); 391 | } 392 | }, 393 | 394 | /** 395 | * @param {Number} f 0..1 396 | * @returns {String} [A-F] 397 | */ 398 | floatToHex (f) { 399 | let hex = Math.round(f * 255).toString(16); 400 | if (hex.length === 1) { 401 | hex = '0' + hex; 402 | } 403 | return hex.toUpperCase(); 404 | }, 405 | 406 | /** 407 | * @param {String} name 408 | * @param {String} hexValue #ffffff, #ffffffff 409 | * @returns {MSColorAsset} MSColorAsset 410 | */ 411 | colorAsset (name, hexValue) { 412 | let mscolor = MSImmutableColor.colorWithSVGString(hexValue).newMutableCounterpart(); 413 | return MSColorAsset.alloc().initWithAsset_name(mscolor, name); 414 | }, 415 | 416 | /** 417 | * @param {String} name 418 | * @param {Number} red 0..1 419 | * @param {Number} green 0..1 420 | * @param {Number} blue 0..1 421 | * @param {Number} alpha 0..1 422 | * @returns {MSColorAsset} MSColorAsset 423 | */ 424 | colorAssetWithName_red_green_blue_alpha (name, red, green, blue, alpha) { 425 | let mscolor = MSColor.colorWithRed_green_blue_alpha(red, green, blue, alpha); 426 | return MSColorAsset.alloc().initWithAsset_name(mscolor, name); 427 | }, 428 | 429 | /** 430 | * @param {NSColorList} colorList 431 | * @returns {Array} [{name, color}] 432 | */ 433 | toArray (colorList) { 434 | let colors = []; 435 | colorList.allKeys().forEach(key => { 436 | let nscolor = colorList.colorWithKey(key); 437 | colors.push({ 438 | name: key, 439 | color: this.toHexValue(nscolor), 440 | }); 441 | }); 442 | return colors; 443 | }, 444 | 445 | /** 446 | * @param {String | Null} name 447 | * @returns {String | Null} String 448 | */ 449 | cleanName (name) { 450 | if (name !== null) { 451 | name = name.replace(/\sCopy(\s\d+)?/, ''); 452 | } 453 | return name; 454 | }, 455 | 456 | /** 457 | * @param {Array} colorArray [{name, color}] 458 | * @param {Object} keyCount 459 | * @returns {String} name: #FFFFFF 460 | */ 461 | toTextContent (colorArray, keyCount) { 462 | let text = ''; 463 | colorArray.forEach(item => { 464 | let name = item.name; 465 | let color = item.color; 466 | if (!isNaN(keyCount[name])) { 467 | keyCount[name] ++; 468 | } else { 469 | keyCount[name] = 1; 470 | } 471 | if (keyCount[name] === 1) { 472 | name = item.name; 473 | } else { 474 | name += ' ' + keyCount[name]; 475 | } 476 | if (/^#[0-9A-F]{6}FF$/i.test(item.color)) { 477 | color = item.color.substr(0, 7); 478 | } 479 | text += name + ': ' + color + '\n'; 480 | }); 481 | return text; 482 | } 483 | 484 | } 485 | -------------------------------------------------------------------------------- /src/lib/gpl-to-colors.js: -------------------------------------------------------------------------------- 1 | import { UI } from 'sketch'; 2 | import { EOL } from 'os'; 3 | import { readFileSync } from '@skpm/fs'; 4 | import color from './color'; 5 | 6 | /** 7 | * Convert GIMP palette file to Array [{name, color}] 8 | * http://www.selapa.net/swatches/colors/fileformats.php#gimp_gpl 9 | * @param {String} filePath 10 | * @returns {Array} [ {name, color} ] 11 | */ 12 | export default function(filePath) { 13 | 14 | let colorContents = readFileSync(filePath, 'utf-8'); 15 | let lines = colorContents.split(EOL); 16 | 17 | if (lines.length < 2 && lines[0] !== 'GIMP Palette') { 18 | UI.message('Error: not a GIMP palette file.'); 19 | return; 20 | } 21 | 22 | let colors = []; 23 | 24 | lines.forEach(line => { 25 | let regExpColor = new RegExp(/(\d+)[\t|\s]+(\d+)[\t|\s]+(\d+)[\t|\s]+(.*)/, ''); 26 | if (regExpColor.test(line)) { 27 | let r = parseInt(line.match(regExpColor)[1]); 28 | let g = parseInt(line.match(regExpColor)[2]); 29 | let b = parseInt(line.match(regExpColor)[3]); 30 | let nscolor = color.colorWithRGBA(r, g, b, 1.0); 31 | let hexValue = color.toHexValue(nscolor); 32 | let colorName = line.match(regExpColor)[4] || null; 33 | colors.push({ 34 | name: colorName, 35 | color: hexValue 36 | }); 37 | } 38 | }); 39 | 40 | return colors; 41 | } -------------------------------------------------------------------------------- /src/lib/is-json-string.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Checking string is a JSON or not. 3 | * @param {String} str 4 | * @returns {Boolean} 5 | */ 6 | export default function(str) { 7 | try { 8 | return JSON.parse(str) && true; 9 | } catch (err) { 10 | return false; 11 | } 12 | } -------------------------------------------------------------------------------- /src/lib/is-zip.js: -------------------------------------------------------------------------------- 1 | import { Buffer } from 'buffer'; 2 | import { readFileSync } from '@skpm/fs'; 3 | 4 | /** 5 | * Checking a file is a ZIP or not. 6 | * @param {String} filePath 7 | * @returns {Boolean} 8 | */ 9 | export default function(filePath) { 10 | let contents = readFileSync(filePath); 11 | let buffer = Buffer.from(contents); 12 | 13 | if (!buffer || buffer.length < 4) { 14 | return false; 15 | } 16 | 17 | return buffer[0] === 0x50 && 18 | buffer[1] === 0x4B && 19 | (buffer[2] === 0x03 || buffer[2] === 0x05 || buffer[2] === 0x07) && 20 | (buffer[3] === 0x04 || buffer[3] === 0x06 || buffer[3] === 0x08); 21 | } -------------------------------------------------------------------------------- /src/lib/sketch-to-colors.js: -------------------------------------------------------------------------------- 1 | import sketch from 'sketch/dom'; 2 | import { UI } from 'sketch'; 3 | import { Document } from 'sketch/dom'; 4 | 5 | /** 6 | * Get colors from Sketch file. 7 | * @param {String} filePath 8 | * @returns {Array} [ {name, color} ] 9 | */ 10 | export default function(filePath) { 11 | 12 | if (sketch.version.sketch >= 92) { 13 | filePath = NSURL.fileURLWithPath(filePath); 14 | } 15 | 16 | // Read data from sketch file. 17 | const error = MOPointer.alloc().init(); 18 | const msDocument = MSDocument.alloc().init(); 19 | msDocument.readFromURL_ofType_error(filePath, 'com.bohemiancoding.sketch.drawing', error); 20 | 21 | if (error.value() !== null) { 22 | UI.message('Error: Not a Sketch file.'); 23 | return; 24 | } 25 | 26 | const document = Document.fromNative(msDocument); 27 | 28 | let colors; 29 | if (document.swatches.length > 0) { 30 | colors = document.swatches.map(item => { 31 | return { 32 | name: item.name, 33 | color: item.color 34 | }; 35 | }); 36 | } else { 37 | colors = document.colors.map(item => { 38 | return { 39 | name: item.name, 40 | color: item.color 41 | }; 42 | }); 43 | } 44 | 45 | return colors; 46 | 47 | } -------------------------------------------------------------------------------- /src/lib/sketchpalette-to-colors.js: -------------------------------------------------------------------------------- 1 | import { readFileSync } from '@skpm/fs'; 2 | import color from './color'; 3 | import isJSONString from './is-json-string'; 4 | 5 | /** 6 | * Get colors from Sketch .sketchpalette file. 7 | * @param {String} filePath 8 | * @returns {Array} [ {name, color} ] 9 | */ 10 | export default function(filePath) { 11 | 12 | let assetContent = readFileSync(filePath, 'utf-8'); 13 | 14 | if (isJSONString(assetContent)) { 15 | let colors = JSON.parse(assetContent).colors; 16 | 17 | colors = colors.map(item => { 18 | let nscolor = color.colorWithRGBA( 19 | item.red * 255, 20 | item.green * 255, 21 | item.blue * 255, 22 | item.alpha 23 | ); 24 | let hexValue = color.toHexValue(nscolor); 25 | return { 26 | name: null, 27 | color: hexValue 28 | }; 29 | }); 30 | 31 | return colors; 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /src/lib/sketchpreset-to-colors.js: -------------------------------------------------------------------------------- 1 | import { readFileSync } from '@skpm/fs'; 2 | import color from './color'; 3 | import isJSONString from './is-json-string'; 4 | import isZip from './is-zip'; 5 | import sketch from 'sketch/dom'; 6 | import { UI } from 'sketch'; 7 | import { toArray } from 'util'; 8 | 9 | /** 10 | * Get colors from Sketch .sketchpreset file. 11 | * @param {String} filePath 12 | * @returns {Array} [ {name, color} ] 13 | */ 14 | export default function(filePath) { 15 | 16 | let assetContent = readFileSync(filePath, 'utf-8'); 17 | 18 | // Sketch < 54 sketchpreset is JSON file 19 | if (isJSONString(assetContent)) { 20 | let colors = JSON.parse(assetContent).root.colorAssets; 21 | 22 | colors = colors.map(item => { 23 | let colorName = item.name; 24 | let nscolor = color.colorWithRGBA( 25 | item.color.red * 255, 26 | item.color.green * 255, 27 | item.color.blue * 255, 28 | item.color.alpha 29 | ); 30 | let hexValue = color.toHexValue(nscolor); 31 | return { 32 | name: colorName, 33 | color: hexValue 34 | }; 35 | }); 36 | 37 | return colors; 38 | } 39 | 40 | // Sketch 54+ sketchpreset is ZIP file 41 | if (isZip(filePath)) { 42 | if (sketch.version.sketch >= 54) { 43 | let nativeAssets = MSPersistentAssetCollection.assetCollectionWithURL(NSURL.fileURLWithPath(filePath)); 44 | let colors = toArray(nativeAssets.colorAssets()).map(asset => { 45 | let hexValue = color.toHexValue(asset.color()); 46 | return { 47 | name: String(asset.name()), 48 | color: hexValue 49 | } 50 | }); 51 | return colors; 52 | } else { 53 | UI.message('This sketch preset file need to open with Sketch 54+.'); 54 | return; 55 | } 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /src/lib/txt-to-colors.js: -------------------------------------------------------------------------------- 1 | import { UI } from 'sketch'; 2 | import { EOL } from 'os'; 3 | import { readFileSync } from '@skpm/fs'; 4 | import color from './color'; 5 | 6 | /** 7 | * Get colors from text file. 8 | * 9 | * white: #FFFFFF 10 | * red: red 11 | * blue: rgb(0, 0, 255) 12 | * transparent blue: rgb(0, 0, 255, 0.5) 13 | * cyan: hsl(180, 100, 50) 14 | * transparent cyan: hsla(180, 100%, 50%, 0.5) 15 | * 16 | * @param {String} filePath 17 | * @returns {Array} [ {name, color} ] 18 | */ 19 | export default function(filePath) { 20 | 21 | let colorContents = readFileSync(filePath, 'utf-8'); 22 | let lines = colorContents.split(EOL); 23 | let colors = []; 24 | 25 | lines.forEach(line => { 26 | let lineRegExp = /^(.*):\s?(.*)$/; 27 | if (lineRegExp.test(line)) { 28 | let lineMatch = line.match(lineRegExp); 29 | let colorName = lineMatch[1]; 30 | let colorValue = lineMatch[2]; 31 | let hexValue; 32 | if (/^#[0-9a-f]{3,8}/i.test(colorValue)) { 33 | hexValue = colorValue.match(/^#[0-9a-f]{3,8}/i)[0]; 34 | } else if (/^rgb[a]?\(.*\)/i.test(colorValue)) { 35 | let rgba = colorValue.match(/^rgb[a]?\((.*)\)/i)[1].split(/,\s?/); 36 | let nsColor = color.colorWithRGBA(parseInt(rgba[0]) || 0, parseInt(rgba[1]) || 0, parseInt(rgba[2]) || 0, parseFloat(rgba[3]) || 1); 37 | hexValue = color.toHexValue(nsColor); 38 | } else if (/^hsl[a]?\(.*\)/i.test(colorValue)) { 39 | let hsla = colorValue.match(/^hsl[a]?\((.*)\)/i)[1].split(/,\s?/); 40 | let nsColor = color.colorWithHSLA(parseInt(hsla[0]) || 0, parseInt(hsla[1]) || 0, parseInt(hsla[2]) || 0, parseFloat(hsla[3]) || 1); 41 | hexValue = color.toHexValue(nsColor); 42 | } else if (color.colorWithName(colorValue)) { 43 | hexValue = color.toHexValue(color.colorWithName(colorValue)); 44 | } 45 | if (hexValue) { 46 | colors.push({ 47 | name: colorName, 48 | color: hexValue 49 | }); 50 | } 51 | } 52 | }); 53 | 54 | if (colors.length === 0) { 55 | UI.message('Invalid format text file.'); 56 | return; 57 | } 58 | 59 | return colors; 60 | } -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "compatibleVersion": 3, 3 | "bundleVersion": 1, 4 | "icon": "icon.png", 5 | "commands": [ 6 | { 7 | "name": "Import Colors to Color Variables", 8 | "identifier": "import-colors-to-color-variables", 9 | "script": "./import.js" 10 | }, 11 | { 12 | "name": "Import Colors as Library", 13 | "identifier": "import-colors-as-library", 14 | "script": "./import.js" 15 | }, 16 | { 17 | "name": "Import Colors and Update Color Variables", 18 | "identifier": "import-colors-and-update-color-variables", 19 | "script": "./import.js" 20 | }, 21 | { 22 | "name": "Convert Colors to .clr File", 23 | "identifier": "convert-colors-to-clr-file", 24 | "script": "./import.js" 25 | }, 26 | { 27 | "name": "Convert Colors to .txt File", 28 | "identifier": "convert-colors-to-txt-file", 29 | "script": "./import.js" 30 | }, 31 | { 32 | "name": "Export Color Variables to .clr File", 33 | "identifier": "export-color-variables-to-clr-file", 34 | "script": "./export.js" 35 | }, 36 | { 37 | "name": "Export Color Variables to .txt File", 38 | "identifier": "export-color-variables-to-txt-file", 39 | "script": "./export.js" 40 | } 41 | ], 42 | "menu": { 43 | "title": "Import Colors", 44 | "items": [ 45 | "import-colors-to-color-variables", 46 | "import-colors-as-library", 47 | "import-colors-and-update-color-variables", 48 | "-", 49 | "convert-colors-to-clr-file", 50 | "convert-colors-to-txt-file", 51 | "-", 52 | "export-color-variables-to-clr-file", 53 | "export-color-variables-to-txt-file" 54 | ] 55 | }, 56 | "name": "Import Colors", 57 | "identifier" : "com.ashung.hung.import-colors-sketch", 58 | "homepage" : "https://github.com/Ashung/import-colors-sketch" 59 | } -------------------------------------------------------------------------------- /webpack.skpm.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (config, entry) { 2 | config.mode = 'production'; 3 | } --------------------------------------------------------------------------------