├── .appcast.xml ├── .gitignore ├── LICENSE ├── README.md ├── assets └── browser-preview.png ├── browser-preview.sketchplugin └── Contents │ ├── Resources │ ├── _webpack_resources │ │ ├── 0531855a70d521874460704a4fb5d7b8.html │ │ ├── 0da9135611d0c9b08721678c0a572156.html │ │ ├── 491a168b9e031f552c0daa0d949862f3.html │ │ ├── 4fdfc32a6196f03f5651365988064b52.html │ │ ├── 58922401316bf4f0abe4ede20aecce35.html │ │ ├── 5e575d27fde5171cbc200ec3cf0c61e7.html │ │ ├── 654d608bf0c0cbdb117aa93fa1933ff7.html │ │ ├── 6a66df137c379464a70f4e305147ffd1.html │ │ ├── 7e9c8e38a6203c04e55980006f0362ae.html │ │ ├── 879cff2870bf3a5452c33252d4b14c9a.html │ │ ├── 88abe080c83d9e022a3bbc621521c7b6.html │ │ ├── 8f48e4adc9ce0faf266476673f1c4602.html │ │ ├── 94ecc92fb33248b708e9b02b3b6ffb59.html │ │ ├── c4a89c5953d7f5e38bebbd847f1da7bd.html │ │ ├── c708672aa38dc6cad7318108a78e7a8c.html │ │ ├── c86757e6fcef13c2a4bf3641801c0ef2.html │ │ ├── c99af74d530c11dd471e04c78b962e3d.html │ │ └── f263f4fdc5c63456e32e3ac864bf3837.html │ └── browser-preview.png │ └── Sketch │ ├── __browser-preview-settings.js │ ├── __browser-preview-settings.js.map │ ├── __browser-preview.js │ ├── __browser-preview.js.map │ ├── browser-preview-settings.js │ ├── browser-preview-settings.js.map │ ├── browser-preview.js │ ├── browser-preview.js.map │ └── manifest.json ├── package-lock.json ├── package.json ├── resources └── settingsView.html └── src ├── browser-preview-settings.js ├── browser-preview.js ├── create-preview.js ├── manifest.json └── sketch-utils.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 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build artifacts 2 | plugin.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) 2018 Lukas Oppermann 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 | # Browser Preview 2 | ![Browser Preview](assets/browser-preview.png) 3 | 4 | > Quickly preview an artboard in your browser. 5 | 6 | This plugin previews the selected artboard in a `2x` resolution in the browsers, using the artboards background color as the websites background color. The artboard is centered horizontally in the browser. 7 | 8 | ## Installation 9 | 10 | ### 🏃 Sketch Runner 11 | Go to the `install` tab in runner and search for `Browser Preview`. 12 | 13 | 14 | 15 | 16 | 17 | ### Manual Installation 18 | 1. Download the [bundle](https://github.com/lukasoppermann/browser-preview/releases/latest) file (first file with the package icon) and extract it. 19 | 2. Double click the `browser-preview.sketchplugin` bundle 20 | 21 | ## Usage 22 | Simply select one or multiple artboards / layers and press `cmd` + `shift` + `p` or select `Preview Artboard` from the Plugin menu. 23 | 24 | You can customize the shortcut using the default mac keyboard settings and assign any shortcut for sketch to `Preview Artboard`. 25 | 26 | If you want your screen to be aligned to the left of the browser window, simply suffix the artboard name with `:left`. E.g. "Desktop" would be "Desktop:left". 27 | 28 | ## Customize browser 29 | Using the settings you can select which browser to use for previewing the artboard. 30 | -------------------------------------------------------------------------------- /assets/browser-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukasoppermann/browser-preview/a308c9ad0500fe5db8d1b71b06a1a1d7d9aa94b5/assets/browser-preview.png -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Resources/_webpack_resources/0531855a70d521874460704a4fb5d7b8.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 116 | 117 | 118 | 119 |
120 |
121 | 122 | 128 |

Choose which browser should be used to preview your designs.

129 |
130 |
131 |
132 | 133 |

Play a sound effect before the design is previewed.

134 |
135 | 138 |
139 | 140 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Resources/_webpack_resources/0da9135611d0c9b08721678c0a572156.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 116 | 117 | 118 | 119 |
120 |
121 | 122 | 128 |

Choose which browser should be used to preview your designs.

129 |
130 |
131 |
132 | 133 |

Play a sound effect before the design is previewed.

134 |
135 | 138 |
139 | 140 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Resources/_webpack_resources/491a168b9e031f552c0daa0d949862f3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 102 | 103 | 104 | 105 |
106 |
107 | 108 | 114 |

Choose which browser should be used to preview your designs.

115 |
116 |
117 |
118 | 119 |

Play a sound effect before the design is previewed.

120 |
121 | 122 |
123 | 124 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Resources/_webpack_resources/4fdfc32a6196f03f5651365988064b52.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 95 | 96 | 97 | 98 |
99 |
100 | 101 | 107 |

Choose which browser should be used to preview your designs.

108 |
109 |
110 | 111 |

Play a sound effect before the design is previewed.

112 |
113 | 114 |
115 | 116 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Resources/_webpack_resources/58922401316bf4f0abe4ede20aecce35.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 108 | 109 | 110 | 111 |
112 |
113 | 114 | 120 |

Choose which browser should be used to preview your designs.

121 |
122 |
123 |
124 | 125 |

Play a sound effect before the design is previewed.

126 |
127 | 128 |
129 | 130 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Resources/_webpack_resources/5e575d27fde5171cbc200ec3cf0c61e7.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 117 | 118 | 119 | 120 |
121 |
122 | 123 | 129 |

Choose which browser should be used to preview your designs.

130 |
131 |
132 |
133 | 134 |

Play a sound effect before the design is previewed.

135 |
136 | 139 |
140 | 141 | 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Resources/_webpack_resources/654d608bf0c0cbdb117aa93fa1933ff7.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 116 | 117 | 118 | 119 |
120 |
121 | 122 | 128 |

Choose which browser should be used to preview your designs.

129 |
130 |
131 |
132 | 133 |

Play a sound effect before the design is previewed.

134 |
135 | 138 |
139 | 140 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Resources/_webpack_resources/6a66df137c379464a70f4e305147ffd1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 123 | 124 | 125 | 126 |
127 |
128 | 129 | 135 |

Choose which browser should be used to preview your designs.

136 |
137 |
138 |
139 | 140 |

Play a sound effect before the design is previewed.

141 |
142 | 143 |
144 | 145 | 189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Resources/_webpack_resources/7e9c8e38a6203c04e55980006f0362ae.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 108 | 109 | 110 | 111 |
112 |
113 | 114 | 120 |

Choose which browser should be used to preview your designs.

121 |
122 |
123 |
124 | 125 |

Play a sound effect before the design is previewed.

126 |
127 | 128 |
129 | 130 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Resources/_webpack_resources/879cff2870bf3a5452c33252d4b14c9a.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 119 | 120 | 121 | 122 |
123 |
124 | 125 | 131 |

Choose which browser should be used to preview your designs.

132 |
133 |
134 |
135 | 136 |

Play a sound effect before the design is previewed.

137 |
138 | 139 |
140 | 141 | 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Resources/_webpack_resources/88abe080c83d9e022a3bbc621521c7b6.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 121 | 122 | 123 | 124 |
125 |
126 | 127 | 133 |

Choose which browser should be used to preview your designs.

134 |
135 |
136 |
137 | 138 |

Play a sound effect before the design is previewed.

139 |
140 | 141 |
142 | 143 | 187 | 188 | 189 | 190 | -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Resources/_webpack_resources/8f48e4adc9ce0faf266476673f1c4602.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 98 | 99 | 100 | 101 |
102 |
103 | 104 | 110 |

Choose which browser should be used to preview your designs.

111 |
112 |
113 |
114 | 115 |

Play a sound effect before the design is previewed.

116 |
117 | 118 |
119 | 120 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Resources/_webpack_resources/94ecc92fb33248b708e9b02b3b6ffb59.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 116 | 117 | 118 | 119 |
120 |
121 | 122 | 128 |

Choose which browser should be used to preview your designs.

129 |
130 |
131 |
132 | 133 |

Play a sound effect before the design is previewed.

134 |
135 | 138 |
139 | 140 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Resources/_webpack_resources/c4a89c5953d7f5e38bebbd847f1da7bd.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 100 | 101 | 102 | 103 |
104 |
105 | 106 | 112 |

Choose which browser should be used to preview your designs.

113 |
114 |
115 |
116 | 117 |

Play a sound effect before the design is previewed.

118 |
119 | 120 |
121 | 122 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Resources/_webpack_resources/c708672aa38dc6cad7318108a78e7a8c.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 116 | 117 | 118 | 119 |
120 |
121 | 122 | 128 |

Choose which browser should be used to preview your designs.

129 |
130 |
131 |
132 | 133 |

Play a sound effect before the design is previewed.

134 |
135 | 138 |
139 | 140 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Resources/_webpack_resources/c86757e6fcef13c2a4bf3641801c0ef2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 95 | 96 | 97 | 98 |
99 |
100 | 101 | 107 |

Choose which browser should be used to preview your designs.

108 |
109 |
110 | 111 |

Play a sound effect before the design is previewed.

112 |
113 | 114 |
115 | 116 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Resources/_webpack_resources/c99af74d530c11dd471e04c78b962e3d.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 95 | 96 | 97 | 98 |
99 |
100 | 101 | 107 |

Choose which browser should be used to preview your designs.

108 |
109 |
110 | 111 |

Play a sound effect before the design is previewed.

112 |
113 | 114 |
115 | 116 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Resources/_webpack_resources/f263f4fdc5c63456e32e3ac864bf3837.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 123 | 124 | 125 | 126 |
127 |
128 | 129 | 135 |

Choose which browser should be used to preview your designs.

136 |
137 |
138 |
139 | 140 |

Play a sound effect before the design is previewed.

141 |
142 | 143 |
144 | 145 | 189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Resources/browser-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukasoppermann/browser-preview/a308c9ad0500fe5db8d1b71b06a1a1d7d9aa94b5/browser-preview.sketchplugin/Contents/Resources/browser-preview.png -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Sketch/__browser-preview.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack://exports/webpack/bootstrap","webpack://exports/./node_modules/@skpm/fs/index.js","webpack://exports/./node_modules/@skpm/fs/utils.js","webpack://exports/./src/browser-preview.js","webpack://exports/./src/create-preview.js","webpack://exports/./src/sketch-utils.js","webpack://exports/external \"buffer\"","webpack://exports/external \"sketch\"","webpack://exports/external \"sketch/settings\""],"names":["Settings","require","sketch","context","options","scales","formats","output","overwriting","browser","settingForKey","sound","document","getSelectedDocument","layers","selectedLayers","artboards","artboardIds","length","showMessage","map","layer","getParentArtboard","undefined","type","name","encodeURIComponent","id","backgroundColor","background","color","bounds","frame","object","filter","artboard","indexOf","push","forEach","previewFile","createPreview","util","fs","export","artboardFileName","replace","file","htmlFile","align","split","pop","trim","html","decodeURI","width","height","writeFileSync","runCommand","command","args","task","NSTask","alloc","init","setLaunchPath_","arguments","launch","waitUntilExit","terminationStatus"],"mappings":";;;;;;;;QAAA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;;QAEA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;;;QAGA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA,0CAA0C,gCAAgC;QAC1E;QACA;;QAEA;QACA;QACA;QACA,wDAAwD,kBAAkB;QAC1E;QACA,iDAAiD,cAAc;QAC/D;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA,yCAAyC,iCAAiC;QAC1E,gHAAgH,mBAAmB,EAAE;QACrI;QACA;;QAEA;QACA;QACA;QACA,2BAA2B,0BAA0B,EAAE;QACvD,iCAAiC,eAAe;QAChD;QACA;QACA;;QAEA;QACA,sDAAsD,+DAA+D;;QAErH;QACA;;;QAGA;QACA;;;;;;;;;;;;AClFA;AACA;AACA,aAAa,mBAAO,CAAC,sBAAQ;AAC7B,YAAY,mBAAO,CAAC,iDAAS;AAC7B;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;;AAEA,mBAAmB,OAAO;AAC1B;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA,iBAAiB,kBAAkB;AACnC;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;;;;;;;;;;;;ACpbA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA,KAAK;AACL;AACA;AACA,KAAK;AACL;AACA;AACA,KAAK;AACL;AACA;AACA,KAAK;AACL;AACA;AACA,KAAK;AACL;AACA;AACA,KAAK;AACL;AACA;;AAEA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA,GAAG;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA,GAAG;;AAEH;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA,sBAAsB,mBAAO,CAAC,iDAAS;AACvC;AACA;AACA;AACA;AACA,OAAO;AACP,KAAK;AACL;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;AC9JA;AAAA;AAAA;AAAA;AACA;;AAEA,IAAIA,QAAQ,GAAGC,mBAAO,CAAC,wCAAD,CAAtB;;AACA,IAAIC,MAAM,GAAGD,mBAAO,CAAC,sBAAD,CAApB;;AAEe,yEAAUE,OAAV,EAAmB;AAC9B;AACA,MAAMC,OAAO,GAAG;AACZC,UAAM,EAAE,GADI;AAEZC,WAAO,EAAE,KAFG;AAGZC,UAAM,EAAE,MAHI;AA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","file":"__browser-preview.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./src/browser-preview.js\");\n","// TODO: async. Should probably be done with NSFileHandle and some notifications\n// TODO: file descriptor. Needs to be done with NSFileHandle\nvar Buffer = require(\"buffer\").Buffer;\nvar utils = require(\"./utils\");\nvar parseStat = utils.parseStat;\nvar fsError = utils.fsError;\nvar fsErrorForPath = utils.fsErrorForPath;\nvar encodingFromOptions = utils.encodingFromOptions;\nvar NOT_IMPLEMENTED = utils.NOT_IMPLEMENTED;\n\nmodule.exports.constants = {\n F_OK: 0,\n R_OK: 4,\n W_OK: 2,\n X_OK: 1,\n};\n\nmodule.exports.access = NOT_IMPLEMENTED(\"access\");\n\nmodule.exports.accessSync = function (path, mode) {\n mode = mode | 0;\n var fileManager = NSFileManager.defaultManager();\n\n switch (mode) {\n case 0:\n canAccess = module.exports.existsSync(path);\n break;\n case 1:\n canAccess = Boolean(Number(fileManager.isExecutableFileAtPath(path)));\n break;\n case 2:\n canAccess = Boolean(Number(fileManager.isWritableFileAtPath(path)));\n break;\n case 3:\n canAccess =\n Boolean(Number(fileManager.isExecutableFileAtPath(path))) &&\n Boolean(Number(fileManager.isWritableFileAtPath(path)));\n break;\n case 4:\n canAccess = Boolean(Number(fileManager.isReadableFileAtPath(path)));\n break;\n case 5:\n canAccess =\n Boolean(Number(fileManager.isReadableFileAtPath(path))) &&\n Boolean(Number(fileManager.isExecutableFileAtPath(path)));\n break;\n case 6:\n canAccess =\n Boolean(Number(fileManager.isReadableFileAtPath(path))) &&\n Boolean(Number(fileManager.isWritableFileAtPath(path)));\n break;\n case 7:\n canAccess =\n Boolean(Number(fileManager.isReadableFileAtPath(path))) &&\n Boolean(Number(fileManager.isWritableFileAtPath(path))) &&\n Boolean(Number(fileManager.isExecutableFileAtPath(path)));\n break;\n }\n\n if (!canAccess) {\n throw new Error(\"Can't access \" + String(path));\n }\n};\n\nmodule.exports.appendFile = NOT_IMPLEMENTED(\"appendFile\");\n\nmodule.exports.appendFileSync = function (file, data, options) {\n if (!module.exports.existsSync(file)) {\n return module.exports.writeFileSync(file, data, options);\n }\n\n var handle = NSFileHandle.fileHandleForWritingAtPath(file);\n handle.seekToEndOfFile();\n\n var encoding = encodingFromOptions(options, \"utf8\");\n\n var nsdata = Buffer.from(\n data,\n encoding === \"NSData\" || encoding === \"buffer\" ? undefined : encoding\n ).toNSData();\n\n handle.writeData(nsdata);\n};\n\nmodule.exports.chmod = NOT_IMPLEMENTED(\"chmod\");\n\nmodule.exports.chmodSync = function (path, mode) {\n var err = MOPointer.alloc().init();\n var fileManager = NSFileManager.defaultManager();\n fileManager.setAttributes_ofItemAtPath_error(\n {\n NSFilePosixPermissions: mode,\n },\n path,\n err\n );\n\n if (err.value() !== null) {\n throw fsErrorForPath(path, undefined, err.value());\n }\n};\n\nmodule.exports.chown = NOT_IMPLEMENTED(\"chown\");\nmodule.exports.chownSync = NOT_IMPLEMENTED(\"chownSync\");\n\nmodule.exports.close = NOT_IMPLEMENTED(\"close\");\nmodule.exports.closeSync = NOT_IMPLEMENTED(\"closeSync\");\n\nmodule.exports.copyFile = NOT_IMPLEMENTED(\"copyFile\");\n\nmodule.exports.copyFileSync = function (path, dest, flags) {\n var err = MOPointer.alloc().init();\n var fileManager = NSFileManager.defaultManager();\n fileManager.copyItemAtPath_toPath_error(path, dest, err);\n\n if (err.value() !== null) {\n throw fsErrorForPath(path, false, err.value());\n }\n};\n\nmodule.exports.createReadStream = NOT_IMPLEMENTED(\"createReadStream\");\nmodule.exports.createWriteStream = NOT_IMPLEMENTED(\"createWriteStream\");\n\nmodule.exports.exists = NOT_IMPLEMENTED(\"exists\");\n\nmodule.exports.existsSync = function (path) {\n var fileManager = NSFileManager.defaultManager();\n return Boolean(Number(fileManager.fileExistsAtPath(path)));\n};\n\nmodule.exports.fchmod = NOT_IMPLEMENTED(\"fchmod\");\nmodule.exports.fchmodSync = NOT_IMPLEMENTED(\"fchmodSync\");\nmodule.exports.fchown = NOT_IMPLEMENTED(\"fchown\");\nmodule.exports.fchownSync = NOT_IMPLEMENTED(\"fchownSync\");\nmodule.exports.fdatasync = NOT_IMPLEMENTED(\"fdatasync\");\nmodule.exports.fdatasyncSync = NOT_IMPLEMENTED(\"fdatasyncSync\");\nmodule.exports.fstat = NOT_IMPLEMENTED(\"fstat\");\nmodule.exports.fstatSync = NOT_IMPLEMENTED(\"fstatSync\");\nmodule.exports.fsync = NOT_IMPLEMENTED(\"fsync\");\nmodule.exports.fsyncSync = NOT_IMPLEMENTED(\"fsyncSync\");\nmodule.exports.ftruncate = NOT_IMPLEMENTED(\"ftruncate\");\nmodule.exports.ftruncateSync = NOT_IMPLEMENTED(\"ftruncateSync\");\nmodule.exports.futimes = NOT_IMPLEMENTED(\"futimes\");\nmodule.exports.futimesSync = NOT_IMPLEMENTED(\"futimesSync\");\n\nmodule.exports.lchmod = NOT_IMPLEMENTED(\"lchmod\");\nmodule.exports.lchmodSync = NOT_IMPLEMENTED(\"lchmodSync\");\nmodule.exports.lchown = NOT_IMPLEMENTED(\"lchown\");\nmodule.exports.lchownSync = NOT_IMPLEMENTED(\"lchownSync\");\n\nmodule.exports.link = NOT_IMPLEMENTED(\"link\");\n\nmodule.exports.linkSync = function (existingPath, newPath) {\n var err = MOPointer.alloc().init();\n var fileManager = NSFileManager.defaultManager();\n fileManager.linkItemAtPath_toPath_error(existingPath, newPath, err);\n\n if (err.value() !== null) {\n throw fsErrorForPath(existingPath, undefined, err.value());\n }\n};\n\nmodule.exports.lstat = NOT_IMPLEMENTED(\"lstat\");\n\nmodule.exports.lstatSync = function (path) {\n var err = MOPointer.alloc().init();\n var fileManager = NSFileManager.defaultManager();\n var result = fileManager.attributesOfItemAtPath_error(path, err);\n\n if (err.value() !== null) {\n throw fsErrorForPath(path, undefined, err.value());\n }\n\n return parseStat(result);\n};\n\nmodule.exports.mkdir = NOT_IMPLEMENTED(\"mkdir\");\n\nmodule.exports.mkdirSync = function (path, options) {\n var mode = 0o777;\n var recursive = false;\n if (options && options.mode) {\n mode = options.mode;\n }\n if (options && options.recursive) {\n recursive = options.recursive;\n }\n if (typeof options === \"number\") {\n mode = options;\n }\n var err = MOPointer.alloc().init();\n var fileManager = NSFileManager.defaultManager();\n fileManager.createDirectoryAtPath_withIntermediateDirectories_attributes_error(\n path,\n recursive,\n {\n NSFilePosixPermissions: mode,\n },\n err\n );\n\n if (err.value() !== null) {\n throw new Error(err.value());\n }\n};\n\nmodule.exports.mkdtemp = NOT_IMPLEMENTED(\"mkdtemp\");\n\nmodule.exports.mkdtempSync = function (path) {\n function makeid() {\n var text = \"\";\n var possible =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n\n for (var i = 0; i < 6; i++)\n text += possible.charAt(Math.floor(Math.random() * possible.length));\n\n return text;\n }\n var tempPath = path + makeid();\n module.exports.mkdirSync(tempPath);\n return tempPath;\n};\n\nmodule.exports.open = NOT_IMPLEMENTED(\"open\");\nmodule.exports.openSync = NOT_IMPLEMENTED(\"openSync\");\n\nmodule.exports.read = NOT_IMPLEMENTED(\"read\");\n\nmodule.exports.readdir = NOT_IMPLEMENTED(\"readdir\");\n\nmodule.exports.readdirSync = function (path, options) {\n var encoding = encodingFromOptions(options, \"utf8\");\n var fileManager = NSFileManager.defaultManager();\n var paths = fileManager.subpathsAtPath(path);\n var arr = [];\n for (var i = 0; i < paths.length; i++) {\n var pathName = paths[i];\n arr.push(encoding === \"buffer\" ? Buffer.from(pathName) : String(pathName));\n }\n return arr;\n};\n\nmodule.exports.readFile = NOT_IMPLEMENTED(\"readFile\");\n\nmodule.exports.readFileSync = function (path, options) {\n var encoding = encodingFromOptions(options, \"buffer\");\n var fileManager = NSFileManager.defaultManager();\n var data = fileManager.contentsAtPath(path);\n if (!data) {\n throw fsErrorForPath(path, false);\n }\n\n var buffer = Buffer.from(data);\n\n if (encoding === \"buffer\") {\n return buffer;\n } else if (encoding === \"NSData\") {\n return buffer.toNSData();\n } else {\n return buffer.toString(encoding);\n }\n};\n\nmodule.exports.readlink = NOT_IMPLEMENTED(\"readlink\");\n\nmodule.exports.readlinkSync = function (path) {\n var err = MOPointer.alloc().init();\n var fileManager = NSFileManager.defaultManager();\n var result = fileManager.destinationOfSymbolicLinkAtPath_error(path, err);\n\n if (err.value() !== null) {\n throw fsErrorForPath(path, undefined, err.value());\n }\n\n return String(result);\n};\n\nmodule.exports.readSync = NOT_IMPLEMENTED(\"readSync\");\n\nmodule.exports.realpath = NOT_IMPLEMENTED(\"realpath\");\nmodule.exports.realpath.native = NOT_IMPLEMENTED(\"realpath.native\");\n\nmodule.exports.realpathSync = function (path) {\n return String(\n NSString.stringWithString(path).stringByResolvingSymlinksInPath()\n );\n};\n\nmodule.exports.realpathSync.native = NOT_IMPLEMENTED(\"realpathSync.native\");\n\nmodule.exports.rename = NOT_IMPLEMENTED(\"rename\");\n\nmodule.exports.renameSync = function (oldPath, newPath) {\n var err = MOPointer.alloc().init();\n var fileManager = NSFileManager.defaultManager();\n fileManager.moveItemAtPath_toPath_error(oldPath, newPath, err);\n\n var error = err.value();\n\n if (error !== null) {\n // if there is already a file, we need to overwrite it\n if (\n String(error.domain()) === \"NSCocoaErrorDomain\" &&\n Number(error.code()) === 516\n ) {\n var err2 = MOPointer.alloc().init();\n fileManager.replaceItemAtURL_withItemAtURL_backupItemName_options_resultingItemURL_error(\n NSURL.fileURLWithPath(newPath),\n NSURL.fileURLWithPath(oldPath),\n null,\n NSFileManagerItemReplacementUsingNewMetadataOnly,\n null,\n err2\n );\n if (err2.value() !== null) {\n throw fsErrorForPath(oldPath, undefined, err2.value());\n }\n } else {\n throw fsErrorForPath(oldPath, undefined, error);\n }\n }\n};\n\nmodule.exports.rmdir = NOT_IMPLEMENTED(\"rmdir\");\n\nmodule.exports.rmdirSync = function (path) {\n var err = MOPointer.alloc().init();\n var fileManager = NSFileManager.defaultManager();\n var isDirectory = module.exports.lstatSync(path).isDirectory();\n if (!isDirectory) {\n throw fsError(\"ENOTDIR\", {\n path: path,\n syscall: \"rmdir\",\n });\n }\n fileManager.removeItemAtPath_error(path, err);\n\n if (err.value() !== null) {\n throw fsErrorForPath(path, true, err.value(), \"rmdir\");\n }\n};\n\nmodule.exports.stat = NOT_IMPLEMENTED(\"stat\");\n\n// the only difference with lstat is that we resolve symlinks\n//\n// > lstat() is identical to stat(), except that if pathname is a symbolic\n// > link, then it returns information about the link itself, not the file\n// > that it refers to.\n// http://man7.org/linux/man-pages/man2/lstat.2.html\nmodule.exports.statSync = function (path) {\n return module.exports.lstatSync(module.exports.realpathSync(path));\n};\n\nmodule.exports.symlink = NOT_IMPLEMENTED(\"symlink\");\n\nmodule.exports.symlinkSync = function (target, path) {\n var err = MOPointer.alloc().init();\n var fileManager = NSFileManager.defaultManager();\n var result = fileManager.createSymbolicLinkAtPath_withDestinationPath_error(\n path,\n target,\n err\n );\n\n if (err.value() !== null) {\n throw new Error(err.value());\n }\n};\n\nmodule.exports.truncate = NOT_IMPLEMENTED(\"truncate\");\n\nmodule.exports.truncateSync = function (path, len) {\n var hFile = NSFileHandle.fileHandleForUpdatingAtPath(sFilePath);\n hFile.truncateFileAtOffset(len || 0);\n hFile.closeFile();\n};\n\nmodule.exports.unlink = NOT_IMPLEMENTED(\"unlink\");\n\nmodule.exports.unlinkSync = function (path) {\n var err = MOPointer.alloc().init();\n var fileManager = NSFileManager.defaultManager();\n var isDirectory = module.exports.lstatSync(path).isDirectory();\n if (isDirectory) {\n throw fsError(\"EPERM\", {\n path: path,\n syscall: \"unlink\",\n });\n }\n var result = fileManager.removeItemAtPath_error(path, err);\n\n if (err.value() !== null) {\n throw fsErrorForPath(path, false, err.value());\n }\n};\n\nmodule.exports.unwatchFile = NOT_IMPLEMENTED(\"unwatchFile\");\n\nmodule.exports.utimes = NOT_IMPLEMENTED(\"utimes\");\n\nmodule.exports.utimesSync = function (path, aTime, mTime) {\n var err = MOPointer.alloc().init();\n var fileManager = NSFileManager.defaultManager();\n var result = fileManager.setAttributes_ofItemAtPath_error(\n {\n NSFileModificationDate: aTime,\n },\n path,\n err\n );\n\n if (err.value() !== null) {\n throw fsErrorForPath(path, undefined, err.value());\n }\n};\n\nmodule.exports.watch = NOT_IMPLEMENTED(\"watch\");\nmodule.exports.watchFile = NOT_IMPLEMENTED(\"watchFile\");\n\nmodule.exports.write = NOT_IMPLEMENTED(\"write\");\n\nmodule.exports.writeFile = NOT_IMPLEMENTED(\"writeFile\");\n\nmodule.exports.writeFileSync = function (path, data, options) {\n var encoding = encodingFromOptions(options, \"utf8\");\n\n var nsdata = Buffer.from(\n data,\n encoding === \"NSData\" || encoding === \"buffer\" ? undefined : encoding\n ).toNSData();\n\n nsdata.writeToFile_atomically(path, true);\n};\n\nmodule.exports.writeSync = NOT_IMPLEMENTED(\"writeSync\");\n","module.exports.parseStat = function parseStat(result) {\n return {\n dev: String(result.NSFileDeviceIdentifier),\n // ino: 48064969, The file system specific \"Inode\" number for the file.\n mode: result.NSFileType | result.NSFilePosixPermissions,\n nlink: Number(result.NSFileReferenceCount),\n uid: String(result.NSFileOwnerAccountID),\n gid: String(result.NSFileGroupOwnerAccountID),\n // rdev: 0, A numeric device identifier if the file is considered \"special\".\n size: Number(result.NSFileSize),\n // blksize: 4096, The file system block size for i/o operations.\n // blocks: 8, The number of blocks allocated for this file.\n atimeMs:\n Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000,\n mtimeMs:\n Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000,\n ctimeMs:\n Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000,\n birthtimeMs:\n Number(result.NSFileCreationDate.timeIntervalSince1970()) * 1000,\n atime: new Date(\n Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000 + 0.5\n ), // the 0.5 comes from the node source. Not sure why it's added but in doubt...\n mtime: new Date(\n Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000 + 0.5\n ),\n ctime: new Date(\n Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000 + 0.5\n ),\n birthtime: new Date(\n Number(result.NSFileCreationDate.timeIntervalSince1970()) * 1000 + 0.5\n ),\n isBlockDevice: function () {\n return result.NSFileType === NSFileTypeBlockSpecial;\n },\n isCharacterDevice: function () {\n return result.NSFileType === NSFileTypeCharacterSpecial;\n },\n isDirectory: function () {\n return result.NSFileType === NSFileTypeDirectory;\n },\n isFIFO: function () {\n return false;\n },\n isFile: function () {\n return result.NSFileType === NSFileTypeRegular;\n },\n isSocket: function () {\n return result.NSFileType === NSFileTypeSocket;\n },\n isSymbolicLink: function () {\n return result.NSFileType === NSFileTypeSymbolicLink;\n },\n };\n};\n\nvar ERRORS = {\n EPERM: {\n message: \"operation not permitted\",\n errno: -1,\n },\n ENOENT: {\n message: \"no such file or directory\",\n errno: -2,\n },\n EACCES: {\n message: \"permission denied\",\n errno: -13,\n },\n ENOTDIR: {\n message: \"not a directory\",\n errno: -20,\n },\n EISDIR: {\n message: \"illegal operation on a directory\",\n errno: -21,\n },\n};\n\nfunction fsError(code, options) {\n var error = new Error(\n code +\n \": \" +\n ERRORS[code].message +\n \", \" +\n (options.syscall || \"\") +\n (options.path ? \" '\" + options.path + \"'\" : \"\")\n );\n\n Object.keys(options).forEach(function (k) {\n error[k] = options[k];\n });\n\n error.code = code;\n error.errno = ERRORS[code].errno;\n\n return error;\n}\n\nmodule.exports.fsError = fsError;\n\nmodule.exports.fsErrorForPath = function fsErrorForPath(\n path,\n shouldBeDir,\n err,\n syscall\n) {\n var fileManager = NSFileManager.defaultManager();\n var doesExist = fileManager.fileExistsAtPath(path);\n if (!doesExist) {\n return fsError(\"ENOENT\", {\n path: path,\n syscall: syscall || \"open\",\n });\n }\n var isReadable = fileManager.isReadableFileAtPath(path);\n if (!isReadable) {\n return fsError(\"EACCES\", {\n path: path,\n syscall: syscall || \"open\",\n });\n }\n if (typeof shouldBeDir !== \"undefined\") {\n var isDirectory = require(\"./index\").lstatSync(path).isDirectory();\n if (isDirectory && !shouldBeDir) {\n return fsError(\"EISDIR\", {\n path: path,\n syscall: syscall || \"read\",\n });\n } else if (!isDirectory && shouldBeDir) {\n return fsError(\"ENOTDIR\", {\n path: path,\n syscall: syscall || \"read\",\n });\n }\n }\n return new Error(err || \"Unknown error while manipulating \" + path);\n};\n\nmodule.exports.encodingFromOptions = function encodingFromOptions(\n options,\n defaultValue\n) {\n return options && options.encoding\n ? String(options.encoding)\n : options\n ? String(options)\n : defaultValue;\n};\n\nmodule.exports.NOT_IMPLEMENTED = function NOT_IMPLEMENTED(name) {\n return function () {\n throw new Error(\n \"fs.\" +\n name +\n \" is not implemented yet. If you feel like implementing it, any contribution will be gladly accepted on https://github.com/skpm/fs\"\n );\n };\n};\n","import * as util from './sketch-utils'\nimport createPreview from './create-preview'\n\nvar Settings = require('sketch/settings')\nvar sketch = require('sketch')\n\nexport default function (context) {\n // export options\n const options = {\n scales: '2',\n formats: 'png',\n output: '/tmp',\n overwriting: true\n }\n let browser = Settings.settingForKey('browserPreference') || 'Default';\n let sound = Settings.settingForKey('soundPreference');\n\n // get sketch document\n const document = sketch.getSelectedDocument()\n // get selected page\n // const page = document.selectedPage\n const layers = document.selectedLayers\n let artboards = []\n let artboardIds = []\n // if no artboard selected\n if (layers.length === 0) {\n context.document.showMessage('⚠️ Please select at least one artboard or layer.');\n return;\n }\n\n if (layers.length >= 1) {\n artboards = layers.layers.map(layer => {\n // get parent Artboard if layer is not an artboard\n if (layer.getParentArtboard() !== undefined) {\n layer = layer.getParentArtboard()\n }\n // return special artboard object\n if (layer.type === 'Artboard') {\n return {\n name: encodeURIComponent(layer.name),\n id: layer.id,\n backgroundColor: layer.background.color,\n bounds: layer.frame,\n object: layer\n }\n }\n })\n .filter(artboard => {\n if (artboardIds.indexOf(artboard.id) > -1) {\n return false\n }\n return artboardIds.push(artboard.id)\n })\n\n context.document.showMessage(`Creating preview for ${artboards.length} artboards.`)\n\n artboards.forEach(artboard => {\n let previewFile = createPreview(artboard, options)\n // play sound if the setting is enabled\n if (sound) {\n util.runCommand(\"/usr/bin/afplay\", [\"/System/Library/Sounds/Glass.aiff\"])\n }\n // open export in browser\n if (browser === 'Default') {\n util.runCommand('/usr/bin/open', [previewFile])\n } else {\n util.runCommand('/usr/bin/open', [\"-a\", browser, previewFile])\n }\n });\n\n // if( layer.type === 'Artboard' ) {\n // let previewFile = createPreview(artboard, options)\n // // play sound\n // util.runCommand(\"/usr/bin/afplay\", [\"/System/Library/Sounds/Glass.aiff\"])\n // // open export in browser\n // if (browser === 'Default') {\n // util.runCommand('/usr/bin/open', [previewFile])\n // } else {\n // util.runCommand('/usr/bin/open', [\"-a\", browser, previewFile])\n // }\n // }\n // )\n\n // const artboard = context.selection.firstObject();\n // if( artboard && artboard.isKindOfClass(MSArtboardGroup) ){\n // // show message\n // context.document.showMessage(`Creating preview for \"${artboard.name()}\" in ${browser}`)\n // // create export file directory\n // let previewFile = createPreview(artboard, options)\n // // play sound\n // util.runCommand(\"/usr/bin/afplay\", [\"/System/Library/Sounds/Glass.aiff\"])\n // // open export in browser\n // if (browser === 'Default') {\n // util.runCommand('/usr/bin/open', [previewFile])\n // } else {\n // util.runCommand('/usr/bin/open', [\"-a\", browser, previewFile])\n // }\n // }\n return;\n }\n}\n","let sketch = require('sketch')\nlet fs = require('@skpm/fs')\n\nexport default (artboard\n /* = {\n object: artboard,\n name: string\n backgroundColor: string,\n bounds: {\n width: px\n height: px\n }\n }*/\n, options) => {\n // export file\n sketch.export(artboard.object, options)\n let artboardFileName = artboard.name.replace(/%2F/gi, '/').replace(/%252F/gi, '/')\n let file = options.output + \"/\" + artboardFileName + \"@\" + options.scales + \"x.\" + options.formats\n let htmlFile = `${options.output}/${artboard.name}.html`\n let align = artboard.name.split(':').pop().trim()\n\n // create html\n let html = `\n \n \n ${decodeURI(artboard.name)}\n \n \n \n
\n \"${artboard.name}\"\n
\n \n `\n // create file\n fs.writeFileSync(htmlFile, html)\n // return file\n return htmlFile\n}\n","export function runCommand(command, args) {\n var task = NSTask.alloc().init();\n task.setLaunchPath_(command);\n task.arguments = args;\n task.launch();\n task.waitUntilExit();\n return (task.terminationStatus() == 0)\n}\n","module.exports = require(\"buffer\");","module.exports = require(\"sketch\");","module.exports = require(\"sketch/settings\");"],"sourceRoot":""} -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Sketch/browser-preview-settings.js: -------------------------------------------------------------------------------- 1 | var that = this; 2 | function __skpm_run (key, context) { 3 | that.context = context; 4 | 5 | var exports = 6 | /******/ (function(modules) { // webpackBootstrap 7 | /******/ // The module cache 8 | /******/ var installedModules = {}; 9 | /******/ 10 | /******/ // The require function 11 | /******/ function __webpack_require__(moduleId) { 12 | /******/ 13 | /******/ // Check if module is in cache 14 | /******/ if(installedModules[moduleId]) { 15 | /******/ return installedModules[moduleId].exports; 16 | /******/ } 17 | /******/ // Create a new module (and put it into the cache) 18 | /******/ var module = installedModules[moduleId] = { 19 | /******/ i: moduleId, 20 | /******/ l: false, 21 | /******/ exports: {} 22 | /******/ }; 23 | /******/ 24 | /******/ // Execute the module function 25 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 26 | /******/ 27 | /******/ // Flag the module as loaded 28 | /******/ module.l = true; 29 | /******/ 30 | /******/ // Return the exports of the module 31 | /******/ return module.exports; 32 | /******/ } 33 | /******/ 34 | /******/ 35 | /******/ // expose the modules object (__webpack_modules__) 36 | /******/ __webpack_require__.m = modules; 37 | /******/ 38 | /******/ // expose the module cache 39 | /******/ __webpack_require__.c = installedModules; 40 | /******/ 41 | /******/ // define getter function for harmony exports 42 | /******/ __webpack_require__.d = function(exports, name, getter) { 43 | /******/ if(!__webpack_require__.o(exports, name)) { 44 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); 45 | /******/ } 46 | /******/ }; 47 | /******/ 48 | /******/ // define __esModule on exports 49 | /******/ __webpack_require__.r = function(exports) { 50 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 51 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 52 | /******/ } 53 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 54 | /******/ }; 55 | /******/ 56 | /******/ // create a fake namespace object 57 | /******/ // mode & 1: value is a module id, require it 58 | /******/ // mode & 2: merge all properties of value into the ns 59 | /******/ // mode & 4: return value when already ns object 60 | /******/ // mode & 8|1: behave like require 61 | /******/ __webpack_require__.t = function(value, mode) { 62 | /******/ if(mode & 1) value = __webpack_require__(value); 63 | /******/ if(mode & 8) return value; 64 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; 65 | /******/ var ns = Object.create(null); 66 | /******/ __webpack_require__.r(ns); 67 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); 68 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); 69 | /******/ return ns; 70 | /******/ }; 71 | /******/ 72 | /******/ // getDefaultExport function for compatibility with non-harmony modules 73 | /******/ __webpack_require__.n = function(module) { 74 | /******/ var getter = module && module.__esModule ? 75 | /******/ function getDefault() { return module['default']; } : 76 | /******/ function getModuleExports() { return module; }; 77 | /******/ __webpack_require__.d(getter, 'a', getter); 78 | /******/ return getter; 79 | /******/ }; 80 | /******/ 81 | /******/ // Object.prototype.hasOwnProperty.call 82 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 83 | /******/ 84 | /******/ // __webpack_public_path__ 85 | /******/ __webpack_require__.p = ""; 86 | /******/ 87 | /******/ 88 | /******/ // Load entry module and return exports 89 | /******/ return __webpack_require__(__webpack_require__.s = "./src/browser-preview-settings.js"); 90 | /******/ }) 91 | /************************************************************************/ 92 | /******/ ({ 93 | 94 | /***/ "./resources/settingsView.html": 95 | /*!*************************************!*\ 96 | !*** ./resources/settingsView.html ***! 97 | \*************************************/ 98 | /*! no static exports found */ 99 | /***/ (function(module, exports) { 100 | 101 | module.exports = "file://" + context.plugin.urlForResourceNamed("_webpack_resources/c86757e6fcef13c2a4bf3641801c0ef2.html").path(); 102 | 103 | /***/ }), 104 | 105 | /***/ "./src/browser-preview-settings.js": 106 | /*!*****************************************!*\ 107 | !*** ./src/browser-preview-settings.js ***! 108 | \*****************************************/ 109 | /*! exports provided: default, getDefaultSettings */ 110 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 111 | 112 | "use strict"; 113 | __webpack_require__.r(__webpack_exports__); 114 | /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getDefaultSettings", function() { return getDefaultSettings; }); 115 | var BrowserWindow = __webpack_require__(!(function webpackMissingModule() { var e = new Error("Cannot find module 'sketch-module-web-view'"); e.code = 'MODULE_NOT_FOUND'; throw e; }())); 116 | 117 | var sketch = __webpack_require__(/*! sketch */ "sketch"); 118 | 119 | var Settings = sketch.Settings; 120 | var UI = sketch.UI; 121 | var settingsKeys = { 122 | BROWSERPREFERENCE: 'browserPreference', 123 | SOUNDPREFERENCE: 'soundPreference' 124 | }; 125 | /* harmony default export */ __webpack_exports__["default"] = (function (context) { 126 | var options = { 127 | identifier: 'browserPreviewSettings', 128 | width: 320, 129 | height: 260, 130 | show: false, 131 | resizable: false, 132 | title: 'Browser Preview - Settings', 133 | minimizable: false, 134 | maximizable: false, 135 | alwaysOnTop: false, 136 | devTools: false, 137 | center: true, 138 | backgroundColor: '#ececec', 139 | hidesOnDeactive: false 140 | }; 141 | var browserWindow = new BrowserWindow(options); 142 | browserWindow.once('ready-to-show', function () { 143 | browserWindow.show(); 144 | }); 145 | var webContents = browserWindow.webContents; 146 | webContents.on('did-start-loading', function () { 147 | var defaultSettings = JSON.stringify(getDefaultSettings()); 148 | browserWindow.webContents.executeJavaScript("window.settings =" + defaultSettings + "; populateSettings();").then(function (res) { 149 | return console.info(res); 150 | }).catch(function (err) { 151 | return console.error(err); 152 | }); 153 | }); 154 | webContents.on('updateSettings', function (data) { 155 | setSettings(data); 156 | browserWindow.close(); 157 | }); 158 | browserWindow.on('closed', function () { 159 | browserWindow = null; 160 | }); 161 | browserWindow.loadURL(__webpack_require__(/*! ../resources/settingsView.html */ "./resources/settingsView.html")); 162 | }); 163 | 164 | function getSettings() { 165 | var obj = {}; 166 | var isUndefined = true; 167 | Object.keys(settingsKeys).forEach(function (key) { 168 | obj[settingsKeys[key]] = Settings.settingForKey(settingsKeys[key]); 169 | isUndefined = obj[settingsKeys[key]] === undefined; 170 | }); 171 | return { 172 | data: obj, 173 | isUndefined: isUndefined 174 | }; 175 | } 176 | 177 | function setSettings(data) { 178 | Object.keys(data).forEach(function (key) { 179 | Settings.setSettingForKey(key, data[key]); 180 | }); 181 | } 182 | 183 | function getDefaultSettings() { 184 | var currentSettings = getSettings(); 185 | 186 | if (currentSettings.isUndefined) { 187 | var obj = {}; 188 | obj[settingsKeys.BROWSERPREFERENCE] = 'safari', obj[settingsKeys.SOUNDPREFERENCE] = false; 189 | setSettings(obj); 190 | return obj; 191 | } else { 192 | return currentSettings.data; 193 | } 194 | } 195 | 196 | /***/ }), 197 | 198 | /***/ "sketch": 199 | /*!*************************!*\ 200 | !*** external "sketch" ***! 201 | \*************************/ 202 | /*! no static exports found */ 203 | /***/ (function(module, exports) { 204 | 205 | module.exports = require("sketch"); 206 | 207 | /***/ }) 208 | 209 | /******/ }); 210 | if (key === 'default' && typeof exports === 'function') { 211 | exports(context); 212 | } else { 213 | exports[key](context); 214 | } 215 | } 216 | that['onRun'] = __skpm_run.bind(this, 'default') 217 | 218 | //# sourceMappingURL=browser-preview-settings.js.map -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Sketch/browser-preview-settings.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack://exports/webpack/bootstrap","webpack://exports/./resources/settingsView.html","webpack://exports/./src/browser-preview-settings.js","webpack://exports/external \"sketch\""],"names":["BrowserWindow","require","sketch","Settings","UI","settingsKeys","BROWSERPREFERENCE","SOUNDPREFERENCE","context","options","identifier","width","height","show","resizable","title","minimizable","maximizable","alwaysOnTop","devTools","center","backgroundColor","hidesOnDeactive","browserWindow","once","webContents","on","defaultSettings","JSON","stringify","getDefaultSettings","executeJavaScript","then","console","info","res","catch","error","err","setSettings","data","close","loadURL","getSettings","obj","isUndefined","Object","keys","forEach","key","settingForKey","undefined","setSettingForKey","currentSettings"],"mappings":"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","file":"browser-preview-settings.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./src/browser-preview-settings.js\");\n","module.exports = \"file://\" + context.plugin.urlForResourceNamed(\"_webpack_resources/c86757e6fcef13c2a4bf3641801c0ef2.html\").path();","const BrowserWindow = require('sketch-module-web-view');\nconst sketch = require('sketch');\n\nconst Settings = sketch.Settings;\nconst UI = sketch.UI;\n\nconst settingsKeys = {\n BROWSERPREFERENCE: 'browserPreference',\n SOUNDPREFERENCE: 'soundPreference'\n};\n\nexport default function (context) {\n const options = {\n identifier: 'browserPreviewSettings',\n width: 320,\n height: 260,\n show: false,\n resizable: false,\n title: 'Browser Preview - Settings',\n minimizable: false,\n maximizable: false,\n alwaysOnTop: false,\n devTools: false,\n center: true,\n backgroundColor: '#ececec',\n hidesOnDeactive: false\n };\n\n let browserWindow = new BrowserWindow(options);\n\n browserWindow.once('ready-to-show', () => {\n browserWindow.show();\n });\n\n const webContents = browserWindow.webContents;\n\n webContents.on('did-start-loading', () => {\n let defaultSettings = JSON.stringify(getDefaultSettings());\n\n browserWindow.webContents\n .executeJavaScript(\n \"window.settings =\" + defaultSettings + \"; populateSettings();\"\n ).then(res => console.info(res))\n .catch(err => console.error(err));\n });\n\n webContents.on('updateSettings', data => {\n setSettings(data);\n browserWindow.close();\n });\n\n browserWindow.on('closed', () => {\n browserWindow = null;\n });\n\n browserWindow.loadURL(require('../resources/settingsView.html'));\n}\n\nfunction getSettings() {\n let obj = {};\n let isUndefined = true;\n\n Object.keys(settingsKeys).forEach(key => {\n obj[settingsKeys[key]] = Settings.settingForKey(settingsKeys[key]);\n isUndefined = obj[settingsKeys[key]] === undefined;\n });\n\n return {\n data: obj,\n isUndefined: isUndefined\n };\n}\n\nfunction setSettings(data) {\n Object.keys(data).forEach(key => {\n Settings.setSettingForKey(key, data[key]);\n });\n}\n\nexport function getDefaultSettings() {\n const currentSettings = getSettings();\n\n if (currentSettings.isUndefined) {\n let obj = {};\n (obj[settingsKeys.BROWSERPREFERENCE] = 'safari'),\n (obj[settingsKeys.SOUNDPREFERENCE] = false);\n setSettings(obj);\n return obj;\n } else {\n return currentSettings.data;\n }\n}\n","module.exports = require(\"sketch\");"],"sourceRoot":""} -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Sketch/browser-preview.js: -------------------------------------------------------------------------------- 1 | var that = this; 2 | function __skpm_run (key, context) { 3 | that.context = context; 4 | 5 | var exports = 6 | /******/ (function(modules) { // webpackBootstrap 7 | /******/ // The module cache 8 | /******/ var installedModules = {}; 9 | /******/ 10 | /******/ // The require function 11 | /******/ function __webpack_require__(moduleId) { 12 | /******/ 13 | /******/ // Check if module is in cache 14 | /******/ if(installedModules[moduleId]) { 15 | /******/ return installedModules[moduleId].exports; 16 | /******/ } 17 | /******/ // Create a new module (and put it into the cache) 18 | /******/ var module = installedModules[moduleId] = { 19 | /******/ i: moduleId, 20 | /******/ l: false, 21 | /******/ exports: {} 22 | /******/ }; 23 | /******/ 24 | /******/ // Execute the module function 25 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 26 | /******/ 27 | /******/ // Flag the module as loaded 28 | /******/ module.l = true; 29 | /******/ 30 | /******/ // Return the exports of the module 31 | /******/ return module.exports; 32 | /******/ } 33 | /******/ 34 | /******/ 35 | /******/ // expose the modules object (__webpack_modules__) 36 | /******/ __webpack_require__.m = modules; 37 | /******/ 38 | /******/ // expose the module cache 39 | /******/ __webpack_require__.c = installedModules; 40 | /******/ 41 | /******/ // define getter function for harmony exports 42 | /******/ __webpack_require__.d = function(exports, name, getter) { 43 | /******/ if(!__webpack_require__.o(exports, name)) { 44 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); 45 | /******/ } 46 | /******/ }; 47 | /******/ 48 | /******/ // define __esModule on exports 49 | /******/ __webpack_require__.r = function(exports) { 50 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 51 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 52 | /******/ } 53 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 54 | /******/ }; 55 | /******/ 56 | /******/ // create a fake namespace object 57 | /******/ // mode & 1: value is a module id, require it 58 | /******/ // mode & 2: merge all properties of value into the ns 59 | /******/ // mode & 4: return value when already ns object 60 | /******/ // mode & 8|1: behave like require 61 | /******/ __webpack_require__.t = function(value, mode) { 62 | /******/ if(mode & 1) value = __webpack_require__(value); 63 | /******/ if(mode & 8) return value; 64 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; 65 | /******/ var ns = Object.create(null); 66 | /******/ __webpack_require__.r(ns); 67 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); 68 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); 69 | /******/ return ns; 70 | /******/ }; 71 | /******/ 72 | /******/ // getDefaultExport function for compatibility with non-harmony modules 73 | /******/ __webpack_require__.n = function(module) { 74 | /******/ var getter = module && module.__esModule ? 75 | /******/ function getDefault() { return module['default']; } : 76 | /******/ function getModuleExports() { return module; }; 77 | /******/ __webpack_require__.d(getter, 'a', getter); 78 | /******/ return getter; 79 | /******/ }; 80 | /******/ 81 | /******/ // Object.prototype.hasOwnProperty.call 82 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 83 | /******/ 84 | /******/ // __webpack_public_path__ 85 | /******/ __webpack_require__.p = ""; 86 | /******/ 87 | /******/ 88 | /******/ // Load entry module and return exports 89 | /******/ return __webpack_require__(__webpack_require__.s = "./src/browser-preview.js"); 90 | /******/ }) 91 | /************************************************************************/ 92 | /******/ ({ 93 | 94 | /***/ "./node_modules/@skpm/fs/index.js": 95 | /*!****************************************!*\ 96 | !*** ./node_modules/@skpm/fs/index.js ***! 97 | \****************************************/ 98 | /*! no static exports found */ 99 | /***/ (function(module, exports) { 100 | 101 | // TODO: async. Should probably be done with NSFileHandle and some notifications 102 | // TODO: file descriptor. Needs to be done with NSFileHandle 103 | 104 | module.exports.constants = { 105 | F_OK: 0, 106 | R_OK: 4, 107 | W_OK: 2, 108 | X_OK: 1 109 | } 110 | 111 | module.exports.accessSync = function(path, mode) { 112 | mode = mode | 0 113 | var fileManager = NSFileManager.defaultManager() 114 | 115 | switch (mode) { 116 | case 0: 117 | return module.exports.existsSync(path) 118 | case 1: 119 | return Boolean(fileManager.isExecutableFileAtPath(path)) 120 | case 2: 121 | return Boolean(fileManager.isWritableFileAtPath(path)) 122 | case 3: 123 | return Boolean(fileManager.isExecutableFileAtPath(path) && fileManager.isWritableFileAtPath(path)) 124 | case 4: 125 | return Boolean(fileManager.isReadableFileAtPath(path)) 126 | case 5: 127 | return Boolean(fileManager.isReadableFileAtPath(path) && fileManager.isExecutableFileAtPath(path)) 128 | case 6: 129 | return Boolean(fileManager.isReadableFileAtPath(path) && fileManager.isWritableFileAtPath(path)) 130 | case 7: 131 | return Boolean(fileManager.isReadableFileAtPath(path) && fileManager.isWritableFileAtPath(path) && fileManager.isExecutableFileAtPath(path)) 132 | } 133 | } 134 | 135 | module.exports.appendFileSync = function(file, data, options) { 136 | if (!module.exports.existsSync(file)) { 137 | return module.exports.writeFileSync(file, data, options) 138 | } 139 | 140 | var handle = NSFileHandle.fileHandleForWritingAtPath(file) 141 | handle.seekToEndOfFile() 142 | 143 | if (data && data.mocha && data.mocha().class() === 'NSData') { 144 | handle.writeData(data) 145 | return 146 | } 147 | 148 | var encoding = options && options.encoding ? options.encoding : (options ? options : 'utf8') 149 | 150 | var string = NSString.stringWithString(data) 151 | var nsdata 152 | 153 | switch (encoding) { 154 | case 'utf8': 155 | nsdata = string.dataUsingEncoding(NSUTF8StringEncoding) 156 | break 157 | case 'ascii': 158 | nsdata = string.dataUsingEncoding(NSASCIIStringEncoding) 159 | break 160 | case 'utf16le': 161 | case 'ucs2': 162 | nsdata = string.dataUsingEncoding(NSUTF16LittleEndianStringEncoding) 163 | break 164 | case 'base64': 165 | var plainData = string.dataUsingEncoding(NSUTF8StringEncoding) 166 | nsdata = plainData.base64EncodedStringWithOptions(0).dataUsingEncoding(NSUTF8StringEncoding) 167 | break 168 | case 'latin1': 169 | case 'binary': 170 | nsdata = string.dataUsingEncoding(NSISOLatin1StringEncoding) 171 | break 172 | case 'hex': 173 | // TODO: how? 174 | default: 175 | nsdata = string.dataUsingEncoding(NSUTF8StringEncoding) 176 | break 177 | } 178 | 179 | handle.writeData(data) 180 | } 181 | 182 | module.exports.chmodSync = function(path, mode) { 183 | var err = MOPointer.alloc().init() 184 | var fileManager = NSFileManager.defaultManager() 185 | fileManager.setAttributes_ofItemAtPath_error({ 186 | NSFilePosixPermissions: mode 187 | }, path, err) 188 | 189 | if (err.value() !== null) { 190 | throw new Error(err.value()) 191 | } 192 | } 193 | 194 | module.exports.copyFileSync = function(path, dest, flags) { 195 | var err = MOPointer.alloc().init() 196 | var fileManager = NSFileManager.defaultManager() 197 | fileManager.copyItemAtPath_toPath_error(path, dest, err) 198 | 199 | if (err.value() !== null) { 200 | throw new Error(err.value()) 201 | } 202 | } 203 | 204 | module.exports.existsSync = function(path) { 205 | var fileManager = NSFileManager.defaultManager() 206 | return Boolean(fileManager.fileExistsAtPath(path)) 207 | } 208 | 209 | module.exports.linkSync = function(existingPath, newPath) { 210 | var err = MOPointer.alloc().init() 211 | var fileManager = NSFileManager.defaultManager() 212 | fileManager.linkItemAtPath_toPath_error(existingPath, newPath, err) 213 | 214 | if (err.value() !== null) { 215 | throw new Error(err.value()) 216 | } 217 | } 218 | 219 | module.exports.mkdirSync = function(path, mode) { 220 | mode = mode || 0o777 221 | var err = MOPointer.alloc().init() 222 | var fileManager = NSFileManager.defaultManager() 223 | fileManager.createDirectoryAtPath_withIntermediateDirectories_attributes_error(path, false, { 224 | NSFilePosixPermissions: mode 225 | }, err) 226 | 227 | if (err.value() !== null) { 228 | throw new Error(err.value()) 229 | } 230 | } 231 | 232 | module.exports.mkdtempSync = function(path) { 233 | function makeid() { 234 | var text = ""; 235 | var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 236 | 237 | for (var i = 0; i < 6; i++) 238 | text += possible.charAt(Math.floor(Math.random() * possible.length)); 239 | 240 | return text; 241 | } 242 | var tempPath = path + makeid() 243 | module.exports.mkdirSync(tempPath) 244 | return tempPath 245 | } 246 | 247 | module.exports.readdirSync = function(path) { 248 | var fileManager = NSFileManager.defaultManager() 249 | var paths = fileManager.subpathsAtPath(path) 250 | var arr = [] 251 | for (var i = 0; i < paths.length; i++) { 252 | arr.push(paths[i]) 253 | } 254 | return arr 255 | } 256 | 257 | module.exports.readFileSync = function(path, options) { 258 | var encoding = options && options.encoding ? options.encoding : (options ? options : 'buffer') 259 | var fileManager = NSFileManager.defaultManager() 260 | var data = fileManager.contentsAtPath(path) 261 | switch (encoding) { 262 | case 'utf8': 263 | return String(NSString.alloc().initWithData_encoding(data, NSUTF8StringEncoding)) 264 | case 'ascii': 265 | return String(NSString.alloc().initWithData_encoding(data, NSASCIIStringEncoding)) 266 | case 'utf16le': 267 | case 'ucs2': 268 | return String(NSString.alloc().initWithData_encoding(data, NSUTF16LittleEndianStringEncoding)) 269 | case 'base64': 270 | var nsdataDecoded = NSData.alloc().initWithBase64EncodedData_options(data, 0) 271 | return String(NSString.alloc().initWithData_encoding(nsdataDecoded, NSUTF8StringEncoding)) 272 | case 'latin1': 273 | case 'binary': 274 | return String(NSString.alloc().initWithData_encoding(data, NSISOLatin1StringEncoding)) 275 | case 'hex': 276 | // TODO: how? 277 | return data 278 | default: 279 | return data 280 | } 281 | } 282 | 283 | module.exports.readlinkSync = function(path) { 284 | var err = MOPointer.alloc().init() 285 | var fileManager = NSFileManager.defaultManager() 286 | var result = fileManager.destinationOfSymbolicLinkAtPath_error(path, err) 287 | 288 | if (err.value() !== null) { 289 | throw new Error(err.value()) 290 | } 291 | 292 | return result 293 | } 294 | 295 | module.exports.realpathSync = function(path) { 296 | return NSString.stringByResolvingSymlinksInPath(path) 297 | } 298 | 299 | module.exports.renameSync = function(oldPath, newPath) { 300 | var err = MOPointer.alloc().init() 301 | var fileManager = NSFileManager.defaultManager() 302 | fileManager.moveItemAtPath_toPath_error(oldPath, newPath, err) 303 | 304 | if (err.value() !== null) { 305 | throw new Error(err.value()) 306 | } 307 | } 308 | 309 | module.exports.rmdirSync = function(path) { 310 | var err = MOPointer.alloc().init() 311 | var fileManager = NSFileManager.defaultManager() 312 | fileManager.removeItemAtPath_error(path, err) 313 | 314 | if (err.value() !== null) { 315 | throw new Error(err.value()) 316 | } 317 | } 318 | 319 | module.exports.statSync = function(path) { 320 | var err = MOPointer.alloc().init() 321 | var fileManager = NSFileManager.defaultManager() 322 | var result = fileManager.attributesOfItemAtPath_error(path, err) 323 | 324 | if (err.value() !== null) { 325 | throw new Error(err.value()) 326 | } 327 | 328 | return { 329 | dev: String(result.NSFileDeviceIdentifier), 330 | // ino: 48064969, The file system specific "Inode" number for the file. 331 | mode: result.NSFileType | result.NSFilePosixPermissions, 332 | nlink: Number(result.NSFileReferenceCount), 333 | uid: String(result.NSFileOwnerAccountID), 334 | gid: String(result.NSFileGroupOwnerAccountID), 335 | // rdev: 0, A numeric device identifier if the file is considered "special". 336 | size: Number(result.NSFileSize), 337 | // blksize: 4096, The file system block size for i/o operations. 338 | // blocks: 8, The number of blocks allocated for this file. 339 | atimeMs: Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000, 340 | mtimeMs: Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000, 341 | ctimeMs: Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000, 342 | birthtimeMs: Number(result.NSFileCreationDate.timeIntervalSince1970()) * 1000, 343 | atime: new Date(Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000 + 0.5), // the 0.5 comes from the node source. Not sure why it's added but in doubt... 344 | mtime: new Date(Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000 + 0.5), 345 | ctime: new Date(Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000 + 0.5), 346 | birthtime: new Date(Number(result.NSFileCreationDate.timeIntervalSince1970()) * 1000 + 0.5), 347 | isBlockDevice: function() { return result.NSFileType === NSFileTypeBlockSpecial }, 348 | isCharacterDevice: function() { return result.NSFileType === NSFileTypeCharacterSpecial }, 349 | isDirectory: function() { return result.NSFileType === NSFileTypeDirectory }, 350 | isFIFO: function() { return false }, 351 | isFile: function() { return result.NSFileType === NSFileTypeRegular }, 352 | isSocket: function() { return result.NSFileType === NSFileTypeSocket }, 353 | isSymbolicLink: function() { return result.NSFileType === NSFileTypeSymbolicLink }, 354 | } 355 | } 356 | 357 | module.exports.symlinkSync = function(target, path) { 358 | var err = MOPointer.alloc().init() 359 | var fileManager = NSFileManager.defaultManager() 360 | var result = fileManager.createSymbolicLinkAtPath_withDestinationPath_error(path, target, err) 361 | 362 | if (err.value() !== null) { 363 | throw new Error(err.value()) 364 | } 365 | } 366 | 367 | module.exports.truncateSync = function(path, len) { 368 | var hFile = NSFileHandle.fileHandleForUpdatingAtPath(sFilePath) 369 | hFile.truncateFileAtOffset(len || 0) 370 | hFile.closeFile() 371 | } 372 | 373 | module.exports.unlinkSync = function(path) { 374 | var err = MOPointer.alloc().init() 375 | var fileManager = NSFileManager.defaultManager() 376 | var result = fileManager.removeItemAtPath_error(path, err) 377 | 378 | if (err.value() !== null) { 379 | throw new Error(err.value()) 380 | } 381 | } 382 | 383 | module.exports.utimesSync = function(path, aTime, mTime) { 384 | var err = MOPointer.alloc().init() 385 | var fileManager = NSFileManager.defaultManager() 386 | var result = fileManager.setAttributes_ofItemAtPath_error({ 387 | NSFileModificationDate: aTime 388 | }, path, err) 389 | 390 | if (err.value() !== null) { 391 | throw new Error(err.value()) 392 | } 393 | } 394 | 395 | module.exports.writeFileSync = function(path, data, options) { 396 | var encoding = options && options.encoding ? options.encoding : (options ? options : 'utf8') 397 | 398 | if (data && data.mocha && data.mocha().class() === 'NSData') { 399 | data.writeToFile_atomically(path, true) 400 | return 401 | } 402 | 403 | var err = MOPointer.alloc().init() 404 | var string = NSString.stringWithString(data) 405 | 406 | switch (encoding) { 407 | case 'utf8': 408 | string.writeToFile_atomically_encoding_error(path, true, NSUTF8StringEncoding, err) 409 | break 410 | case 'ascii': 411 | string.writeToFile_atomically_encoding_error(path, true, NSASCIIStringEncoding, err) 412 | break 413 | case 'utf16le': 414 | case 'ucs2': 415 | string.writeToFile_atomically_encoding_error(path, true, NSUTF16LittleEndianStringEncoding, err) 416 | break 417 | case 'base64': 418 | var plainData = string.dataUsingEncoding(NSUTF8StringEncoding) 419 | var nsdataEncoded = plainData.base64EncodedStringWithOptions(0) 420 | nsdataEncoded.writeToFile_atomically(path, true) 421 | break 422 | case 'latin1': 423 | case 'binary': 424 | string.writeToFile_atomically_encoding_error(path, true, NSISOLatin1StringEncoding, err) 425 | break 426 | case 'hex': 427 | // TODO: how? 428 | default: 429 | string.writeToFile_atomically_encoding_error(path, true, NSUTF8StringEncoding, err) 430 | break 431 | } 432 | 433 | if (err.value() !== null) { 434 | throw new Error(err.value()) 435 | } 436 | } 437 | 438 | 439 | /***/ }), 440 | 441 | /***/ "./src/browser-preview.js": 442 | /*!********************************!*\ 443 | !*** ./src/browser-preview.js ***! 444 | \********************************/ 445 | /*! exports provided: default */ 446 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 447 | 448 | "use strict"; 449 | __webpack_require__.r(__webpack_exports__); 450 | /* harmony import */ var _sketch_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sketch-utils */ "./src/sketch-utils.js"); 451 | /* harmony import */ var _create_preview__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./create-preview */ "./src/create-preview.js"); 452 | 453 | 454 | 455 | var Settings = __webpack_require__(/*! sketch/settings */ "sketch/settings"); 456 | 457 | var sketch = __webpack_require__(/*! sketch */ "sketch"); 458 | 459 | /* harmony default export */ __webpack_exports__["default"] = (function (context) { 460 | // export options 461 | var options = { 462 | scales: '2', 463 | formats: 'png', 464 | output: '/tmp', 465 | overwriting: true 466 | }; 467 | var browser = Settings.settingForKey('browserPreference') || 'Default'; 468 | var sound = Settings.settingForKey('soundPreference'); // get sketch document 469 | 470 | var document = sketch.getSelectedDocument(); // get selected page 471 | // const page = document.selectedPage 472 | 473 | var layers = document.selectedLayers; 474 | var artboards = []; 475 | var artboardIds = []; // if no artboard selected 476 | 477 | if (layers.length === 0) { 478 | context.document.showMessage('⚠️ Please select at least one artboard or layer.'); 479 | return; 480 | } 481 | 482 | if (layers.length >= 1) { 483 | artboards = layers.layers.map(function (layer) { 484 | // get parent Artboard if layer is not an artboard 485 | if (layer.getParentArtboard() !== undefined) { 486 | layer = layer.getParentArtboard(); 487 | } // return special artboard object 488 | 489 | 490 | if (layer.type === 'Artboard') { 491 | return { 492 | name: encodeURIComponent(layer.name), 493 | id: layer.id, 494 | backgroundColor: layer.background.color, 495 | bounds: layer.frame, 496 | object: layer 497 | }; 498 | } 499 | }).filter(function (artboard) { 500 | if (artboardIds.indexOf(artboard.id) > -1) { 501 | return false; 502 | } 503 | 504 | return artboardIds.push(artboard.id); 505 | }); 506 | context.document.showMessage("Creating preview for ".concat(artboards.length, " artboards.")); 507 | artboards.forEach(function (artboard) { 508 | var previewFile = Object(_create_preview__WEBPACK_IMPORTED_MODULE_1__["default"])(artboard, options); // play sound if the setting is enabled 509 | 510 | if (sound) { 511 | _sketch_utils__WEBPACK_IMPORTED_MODULE_0__["runCommand"]("/usr/bin/afplay", ["/System/Library/Sounds/Glass.aiff"]); 512 | } // open export in browser 513 | 514 | 515 | if (browser === 'Default') { 516 | _sketch_utils__WEBPACK_IMPORTED_MODULE_0__["runCommand"]('/usr/bin/open', [previewFile]); 517 | } else { 518 | _sketch_utils__WEBPACK_IMPORTED_MODULE_0__["runCommand"]('/usr/bin/open', ["-a", browser, previewFile]); 519 | } 520 | }); // if( layer.type === 'Artboard' ) { 521 | // let previewFile = createPreview(artboard, options) 522 | // // play sound 523 | // util.runCommand("/usr/bin/afplay", ["/System/Library/Sounds/Glass.aiff"]) 524 | // // open export in browser 525 | // if (browser === 'Default') { 526 | // util.runCommand('/usr/bin/open', [previewFile]) 527 | // } else { 528 | // util.runCommand('/usr/bin/open', ["-a", browser, previewFile]) 529 | // } 530 | // } 531 | // ) 532 | // const artboard = context.selection.firstObject(); 533 | // if( artboard && artboard.isKindOfClass(MSArtboardGroup) ){ 534 | // // show message 535 | // context.document.showMessage(`Creating preview for "${artboard.name()}" in ${browser}`) 536 | // // create export file directory 537 | // let previewFile = createPreview(artboard, options) 538 | // // play sound 539 | // util.runCommand("/usr/bin/afplay", ["/System/Library/Sounds/Glass.aiff"]) 540 | // // open export in browser 541 | // if (browser === 'Default') { 542 | // util.runCommand('/usr/bin/open', [previewFile]) 543 | // } else { 544 | // util.runCommand('/usr/bin/open', ["-a", browser, previewFile]) 545 | // } 546 | // } 547 | 548 | return; 549 | } 550 | }); 551 | 552 | /***/ }), 553 | 554 | /***/ "./src/create-preview.js": 555 | /*!*******************************!*\ 556 | !*** ./src/create-preview.js ***! 557 | \*******************************/ 558 | /*! exports provided: default */ 559 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 560 | 561 | "use strict"; 562 | __webpack_require__.r(__webpack_exports__); 563 | var sketch = __webpack_require__(/*! sketch */ "sketch"); 564 | 565 | var fs = __webpack_require__(/*! @skpm/fs */ "./node_modules/@skpm/fs/index.js"); 566 | 567 | /* harmony default export */ __webpack_exports__["default"] = (function (artboard 568 | /* = { 569 | object: artboard, 570 | name: string 571 | backgroundColor: string, 572 | bounds: { 573 | width: px 574 | height: px 575 | } 576 | }*/ 577 | , options) { 578 | // export file 579 | sketch.export(artboard.object, options); 580 | var file = options.output + "/" + artboard.name + "@" + options.scales + "x." + options.formats; 581 | var htmlFile = "".concat(options.output, "/").concat(artboard.name, ".html"); 582 | var align = artboard.name.split(':').pop().trim(); // create html 583 | 584 | var html = "\n \n \n ".concat(artboard.name, "\n \n \n \n
\n \"").concat(artboard.name,\n \n "); // create file 585 | 586 | fs.writeFileSync(htmlFile, html); // return file 587 | 588 | return htmlFile; 589 | }); 590 | 591 | /***/ }), 592 | 593 | /***/ "./src/sketch-utils.js": 594 | /*!*****************************!*\ 595 | !*** ./src/sketch-utils.js ***! 596 | \*****************************/ 597 | /*! exports provided: runCommand */ 598 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 599 | 600 | "use strict"; 601 | __webpack_require__.r(__webpack_exports__); 602 | /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "runCommand", function() { return runCommand; }); 603 | function runCommand(command, args) { 604 | var task = NSTask.alloc().init(); 605 | task.setLaunchPath_(command); 606 | task.arguments = args; 607 | task.launch(); 608 | task.waitUntilExit(); 609 | return task.terminationStatus() == 0; 610 | } 611 | 612 | /***/ }), 613 | 614 | /***/ "sketch": 615 | /*!*************************!*\ 616 | !*** external "sketch" ***! 617 | \*************************/ 618 | /*! no static exports found */ 619 | /***/ (function(module, exports) { 620 | 621 | module.exports = require("sketch"); 622 | 623 | /***/ }), 624 | 625 | /***/ "sketch/settings": 626 | /*!**********************************!*\ 627 | !*** external "sketch/settings" ***! 628 | \**********************************/ 629 | /*! no static exports found */ 630 | /***/ (function(module, exports) { 631 | 632 | module.exports = require("sketch/settings"); 633 | 634 | /***/ }) 635 | 636 | /******/ }); 637 | if (key === 'default' && typeof exports === 'function') { 638 | exports(context); 639 | } else { 640 | exports[key](context); 641 | } 642 | } 643 | that['onRun'] = __skpm_run.bind(this, 'default') 644 | 645 | //# sourceMappingURL=browser-preview.js.map -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Sketch/browser-preview.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack://exports/webpack/bootstrap","webpack://exports/./node_modules/@skpm/fs/index.js","webpack://exports/./src/browser-preview.js","webpack://exports/./src/create-preview.js","webpack://exports/./src/sketch-utils.js","webpack://exports/external \"sketch\"","webpack://exports/external \"sketch/settings\""],"names":["Settings","require","sketch","context","options","scales","formats","output","overwriting","browser","settingForKey","sound","document","getSelectedDocument","layers","selectedLayers","artboards","artboardIds","length","showMessage","map","layer","getParentArtboard","undefined","type","name","encodeURIComponent","id","backgroundColor","background","color","bounds","frame","object","filter","indexOf","artboard","push","forEach","previewFile","createPreview","util","fs","export","file","htmlFile","align","split","pop","trim","html","width","height","writeFileSync","runCommand","command","args","task","NSTask","alloc","init","setLaunchPath_","arguments","launch","waitUntilExit","terminationStatus"],"mappings":";;;;;;AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,kDAA0C,gCAAgC;AAC1E;AACA;;AAEA;AACA;AACA;AACA,gEAAwD,kBAAkB;AAC1E;AACA,yDAAiD,cAAc;AAC/D;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iDAAyC,iCAAiC;AAC1E,wHAAgH,mBAAmB,EAAE;AACrI;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;;AAGA;AACA;;;;;;;;;;;;AClFA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA,mBAAmB,OAAO;AAC1B;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,iBAAiB,kBAAkB;AACnC;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,+BAA+B,sDAAsD;AACrF,mCAAmC,0DAA0D;AAC7F,6BAA6B,mDAAmD;AAChF,wBAAwB,eAAe;AACvC,wBAAwB,iDAAiD;AACzE,0BAA0B,gDAAgD;AAC1E,gCAAgC,sDAAsD;AACtF;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;;;;;;;;;;;;;;;AC/UA;AACA;;AAEA,IAAIA,WAAW,mBAAAC,CAAQ,wCAAR,CAAf;;AACA,IAAIC,SAAS,mBAAAD,CAAQ,sBAAR,CAAb;;AAEA,+DAAe,UAAUE,OAAV,EAAmB;AAC9B;AACA,MAAMC,UAAU;AACZC,YAAQ,GADI;AAEZC,aAAS,KAFG;AAGZC,YAAQ,MAHI;AAIZC,iBAAa;AAJD,GAAhB;AAMA,MAAIC,UAAUT,SAASU,aAAT,CAAuB,mBAAv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","file":"browser-preview.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./src/browser-preview.js\");\n","// TODO: async. Should probably be done with NSFileHandle and some notifications\n// TODO: file descriptor. Needs to be done with NSFileHandle\n\nmodule.exports.constants = {\n F_OK: 0,\n R_OK: 4,\n W_OK: 2,\n X_OK: 1\n}\n\nmodule.exports.accessSync = function(path, mode) {\n mode = mode | 0\n var fileManager = NSFileManager.defaultManager()\n\n switch (mode) {\n case 0:\n return module.exports.existsSync(path)\n case 1:\n return Boolean(fileManager.isExecutableFileAtPath(path))\n case 2:\n return Boolean(fileManager.isWritableFileAtPath(path))\n case 3:\n return Boolean(fileManager.isExecutableFileAtPath(path) && fileManager.isWritableFileAtPath(path))\n case 4:\n return Boolean(fileManager.isReadableFileAtPath(path))\n case 5:\n return Boolean(fileManager.isReadableFileAtPath(path) && fileManager.isExecutableFileAtPath(path))\n case 6:\n return Boolean(fileManager.isReadableFileAtPath(path) && fileManager.isWritableFileAtPath(path))\n case 7:\n return Boolean(fileManager.isReadableFileAtPath(path) && fileManager.isWritableFileAtPath(path) && fileManager.isExecutableFileAtPath(path))\n }\n}\n\nmodule.exports.appendFileSync = function(file, data, options) {\n if (!module.exports.existsSync(file)) {\n return module.exports.writeFileSync(file, data, options)\n }\n\n var handle = NSFileHandle.fileHandleForWritingAtPath(file)\n handle.seekToEndOfFile()\n\n if (data && data.mocha && data.mocha().class() === 'NSData') {\n handle.writeData(data)\n return\n }\n\n var encoding = options && options.encoding ? options.encoding : (options ? options : 'utf8')\n\n var string = NSString.stringWithString(data)\n var nsdata\n\n switch (encoding) {\n case 'utf8':\n nsdata = string.dataUsingEncoding(NSUTF8StringEncoding)\n break\n case 'ascii':\n nsdata = string.dataUsingEncoding(NSASCIIStringEncoding)\n break\n case 'utf16le':\n case 'ucs2':\n nsdata = string.dataUsingEncoding(NSUTF16LittleEndianStringEncoding)\n break\n case 'base64':\n var plainData = string.dataUsingEncoding(NSUTF8StringEncoding)\n nsdata = plainData.base64EncodedStringWithOptions(0).dataUsingEncoding(NSUTF8StringEncoding)\n break\n case 'latin1':\n case 'binary':\n nsdata = string.dataUsingEncoding(NSISOLatin1StringEncoding)\n break\n case 'hex':\n // TODO: how?\n default:\n nsdata = string.dataUsingEncoding(NSUTF8StringEncoding)\n break\n }\n\n handle.writeData(data)\n}\n\nmodule.exports.chmodSync = function(path, mode) {\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n fileManager.setAttributes_ofItemAtPath_error({\n NSFilePosixPermissions: mode\n }, path, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n}\n\nmodule.exports.copyFileSync = function(path, dest, flags) {\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n fileManager.copyItemAtPath_toPath_error(path, dest, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n}\n\nmodule.exports.existsSync = function(path) {\n var fileManager = NSFileManager.defaultManager()\n return Boolean(fileManager.fileExistsAtPath(path))\n}\n\nmodule.exports.linkSync = function(existingPath, newPath) {\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n fileManager.linkItemAtPath_toPath_error(existingPath, newPath, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n}\n\nmodule.exports.mkdirSync = function(path, mode) {\n mode = mode || 0o777\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n fileManager.createDirectoryAtPath_withIntermediateDirectories_attributes_error(path, false, {\n NSFilePosixPermissions: mode\n }, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n}\n\nmodule.exports.mkdtempSync = function(path) {\n function makeid() {\n var text = \"\";\n var possible = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n\n for (var i = 0; i < 6; i++)\n text += possible.charAt(Math.floor(Math.random() * possible.length));\n\n return text;\n }\n var tempPath = path + makeid()\n module.exports.mkdirSync(tempPath)\n return tempPath\n}\n\nmodule.exports.readdirSync = function(path) {\n var fileManager = NSFileManager.defaultManager()\n var paths = fileManager.subpathsAtPath(path)\n var arr = []\n for (var i = 0; i < paths.length; i++) {\n arr.push(paths[i])\n }\n return arr\n}\n\nmodule.exports.readFileSync = function(path, options) {\n var encoding = options && options.encoding ? options.encoding : (options ? options : 'buffer')\n var fileManager = NSFileManager.defaultManager()\n var data = fileManager.contentsAtPath(path)\n switch (encoding) {\n case 'utf8':\n return String(NSString.alloc().initWithData_encoding(data, NSUTF8StringEncoding))\n case 'ascii':\n return String(NSString.alloc().initWithData_encoding(data, NSASCIIStringEncoding))\n case 'utf16le':\n case 'ucs2':\n return String(NSString.alloc().initWithData_encoding(data, NSUTF16LittleEndianStringEncoding))\n case 'base64':\n var nsdataDecoded = NSData.alloc().initWithBase64EncodedData_options(data, 0)\n return String(NSString.alloc().initWithData_encoding(nsdataDecoded, NSUTF8StringEncoding))\n case 'latin1':\n case 'binary':\n return String(NSString.alloc().initWithData_encoding(data, NSISOLatin1StringEncoding))\n case 'hex':\n // TODO: how?\n return data\n default:\n return data\n }\n}\n\nmodule.exports.readlinkSync = function(path) {\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n var result = fileManager.destinationOfSymbolicLinkAtPath_error(path, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n\n return result\n}\n\nmodule.exports.realpathSync = function(path) {\n return NSString.stringByResolvingSymlinksInPath(path)\n}\n\nmodule.exports.renameSync = function(oldPath, newPath) {\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n fileManager.moveItemAtPath_toPath_error(oldPath, newPath, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n}\n\nmodule.exports.rmdirSync = function(path) {\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n fileManager.removeItemAtPath_error(path, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n}\n\nmodule.exports.statSync = function(path) {\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n var result = fileManager.attributesOfItemAtPath_error(path, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n\n return {\n dev: String(result.NSFileDeviceIdentifier),\n // ino: 48064969, The file system specific \"Inode\" number for the file.\n mode: result.NSFileType | result.NSFilePosixPermissions,\n nlink: Number(result.NSFileReferenceCount),\n uid: String(result.NSFileOwnerAccountID),\n gid: String(result.NSFileGroupOwnerAccountID),\n // rdev: 0, A numeric device identifier if the file is considered \"special\".\n size: Number(result.NSFileSize),\n // blksize: 4096, The file system block size for i/o operations.\n // blocks: 8, The number of blocks allocated for this file.\n atimeMs: Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000,\n mtimeMs: Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000,\n ctimeMs: Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000,\n birthtimeMs: Number(result.NSFileCreationDate.timeIntervalSince1970()) * 1000,\n atime: new Date(Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000 + 0.5), // the 0.5 comes from the node source. Not sure why it's added but in doubt...\n mtime: new Date(Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000 + 0.5),\n ctime: new Date(Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000 + 0.5),\n birthtime: new Date(Number(result.NSFileCreationDate.timeIntervalSince1970()) * 1000 + 0.5),\n isBlockDevice: function() { return result.NSFileType === NSFileTypeBlockSpecial },\n isCharacterDevice: function() { return result.NSFileType === NSFileTypeCharacterSpecial },\n isDirectory: function() { return result.NSFileType === NSFileTypeDirectory },\n isFIFO: function() { return false },\n isFile: function() { return result.NSFileType === NSFileTypeRegular },\n isSocket: function() { return result.NSFileType === NSFileTypeSocket },\n isSymbolicLink: function() { return result.NSFileType === NSFileTypeSymbolicLink },\n }\n}\n\nmodule.exports.symlinkSync = function(target, path) {\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n var result = fileManager.createSymbolicLinkAtPath_withDestinationPath_error(path, target, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n}\n\nmodule.exports.truncateSync = function(path, len) {\n var hFile = NSFileHandle.fileHandleForUpdatingAtPath(sFilePath)\n hFile.truncateFileAtOffset(len || 0)\n hFile.closeFile()\n}\n\nmodule.exports.unlinkSync = function(path) {\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n var result = fileManager.removeItemAtPath_error(path, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n}\n\nmodule.exports.utimesSync = function(path, aTime, mTime) {\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n var result = fileManager.setAttributes_ofItemAtPath_error({\n NSFileModificationDate: aTime\n }, path, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n}\n\nmodule.exports.writeFileSync = function(path, data, options) {\n var encoding = options && options.encoding ? options.encoding : (options ? options : 'utf8')\n\n if (data && data.mocha && data.mocha().class() === 'NSData') {\n data.writeToFile_atomically(path, true)\n return\n }\n\n var err = MOPointer.alloc().init()\n var string = NSString.stringWithString(data)\n\n switch (encoding) {\n case 'utf8':\n string.writeToFile_atomically_encoding_error(path, true, NSUTF8StringEncoding, err)\n break\n case 'ascii':\n string.writeToFile_atomically_encoding_error(path, true, NSASCIIStringEncoding, err)\n break\n case 'utf16le':\n case 'ucs2':\n string.writeToFile_atomically_encoding_error(path, true, NSUTF16LittleEndianStringEncoding, err)\n break\n case 'base64':\n var plainData = string.dataUsingEncoding(NSUTF8StringEncoding)\n var nsdataEncoded = plainData.base64EncodedStringWithOptions(0)\n nsdataEncoded.writeToFile_atomically(path, true)\n break\n case 'latin1':\n case 'binary':\n string.writeToFile_atomically_encoding_error(path, true, NSISOLatin1StringEncoding, err)\n break\n case 'hex':\n // TODO: how?\n default:\n string.writeToFile_atomically_encoding_error(path, true, NSUTF8StringEncoding, err)\n break\n }\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n}\n","import * as util from './sketch-utils'\nimport createPreview from './create-preview'\n\nvar Settings = require('sketch/settings')\nvar sketch = require('sketch')\n\nexport default function (context) {\n // export options\n const options = {\n scales: '2',\n formats: 'png',\n output: '/tmp',\n overwriting: true\n }\n let browser = Settings.settingForKey('browserPreference') || 'Default';\n let sound = Settings.settingForKey('soundPreference');\n\n // get sketch document\n const document = sketch.getSelectedDocument()\n // get selected page\n // const page = document.selectedPage\n const layers = document.selectedLayers\n let artboards = []\n let artboardIds = []\n // if no artboard selected\n if (layers.length === 0) {\n context.document.showMessage('⚠️ Please select at least one artboard or layer.');\n return;\n }\n\n if (layers.length >= 1) {\n artboards = layers.layers.map(layer => {\n // get parent Artboard if layer is not an artboard\n if (layer.getParentArtboard() !== undefined) {\n layer = layer.getParentArtboard()\n }\n // return special artboard object\n if (layer.type === 'Artboard') {\n return {\n name: encodeURIComponent(layer.name),\n id: layer.id,\n backgroundColor: layer.background.color,\n bounds: layer.frame,\n object: layer\n }\n }\n })\n .filter(artboard => {\n if (artboardIds.indexOf(artboard.id) > -1) {\n return false\n }\n return artboardIds.push(artboard.id)\n })\n\n context.document.showMessage(`Creating preview for ${artboards.length} artboards.`)\n\n artboards.forEach(artboard => {\n let previewFile = createPreview(artboard, options)\n // play sound if the setting is enabled\n if (sound) {\n util.runCommand(\"/usr/bin/afplay\", [\"/System/Library/Sounds/Glass.aiff\"])\n }\n // open export in browser\n if (browser === 'Default') {\n util.runCommand('/usr/bin/open', [previewFile])\n } else {\n util.runCommand('/usr/bin/open', [\"-a\", browser, previewFile])\n }\n });\n\n // if( layer.type === 'Artboard' ) {\n // let previewFile = createPreview(artboard, options)\n // // play sound\n // util.runCommand(\"/usr/bin/afplay\", [\"/System/Library/Sounds/Glass.aiff\"])\n // // open export in browser\n // if (browser === 'Default') {\n // util.runCommand('/usr/bin/open', [previewFile])\n // } else {\n // util.runCommand('/usr/bin/open', [\"-a\", browser, previewFile])\n // }\n // }\n // )\n\n // const artboard = context.selection.firstObject();\n // if( artboard && artboard.isKindOfClass(MSArtboardGroup) ){\n // // show message\n // context.document.showMessage(`Creating preview for \"${artboard.name()}\" in ${browser}`)\n // // create export file directory\n // let previewFile = createPreview(artboard, options)\n // // play sound\n // util.runCommand(\"/usr/bin/afplay\", [\"/System/Library/Sounds/Glass.aiff\"])\n // // open export in browser\n // if (browser === 'Default') {\n // util.runCommand('/usr/bin/open', [previewFile])\n // } else {\n // util.runCommand('/usr/bin/open', [\"-a\", browser, previewFile])\n // }\n // }\n return;\n }\n}\n","let sketch = require('sketch')\nlet fs = require('@skpm/fs')\n\nexport default (artboard\n /* = {\n object: artboard,\n name: string\n backgroundColor: string,\n bounds: {\n width: px\n height: px\n }\n }*/\n, options) => {\n // export file\n sketch.export(artboard.object, options)\n let file = options.output + \"/\" + artboard.name + \"@\" + options.scales + \"x.\" + options.formats\n let htmlFile = `${options.output}/${artboard.name}.html`\n let align = artboard.name.split(':').pop().trim()\n\n // create html\n let html = `\n \n \n ${artboard.name}\n \n \n \n
\n \"${artboard.name}\"\n \n `\n // create file\n fs.writeFileSync(htmlFile, html)\n // return file\n return htmlFile\n}\n","export function runCommand(command, args) {\n var task = NSTask.alloc().init();\n task.setLaunchPath_(command);\n task.arguments = args;\n task.launch();\n task.waitUntilExit();\n return (task.terminationStatus() == 0)\n}\n","module.exports = require(\"sketch\");","module.exports = require(\"sketch/settings\");"],"sourceRoot":""} -------------------------------------------------------------------------------- /browser-preview.sketchplugin/Contents/Sketch/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "compatibleVersion": 3, 3 | "bundleVersion": 1, 4 | "identifier": "com.lukasoppermann.sketch.browser-preview", 5 | "name": "🔍 Browser Preview", 6 | "description": "Quickly preview an artboard in your browser.", 7 | "author": "Lukas Oppermann", 8 | "icon": "browser-preview.png", 9 | "commands": [ 10 | { 11 | "name": "Preview Artboard", 12 | "identifier": "browser-preview", 13 | "script": "__browser-preview.js", 14 | "shortcut": "cmd shift p" 15 | }, 16 | { 17 | "name": "Settings", 18 | "identifier": "browser-preview-settings", 19 | "script": "__browser-preview-settings.js" 20 | } 21 | ], 22 | "menu": { 23 | "title": "🔍 Browser Preview", 24 | "items": [ 25 | "browser-preview", 26 | "browser-preview-settings" 27 | ] 28 | }, 29 | "version": "2.3.1", 30 | "disableCocoaScriptPreprocessor": true, 31 | "appcast": "https://raw.githubusercontent.com/lukasoppermann/browser-preview/master/.appcast.xml" 32 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "browser-preview", 3 | "version": "2.3.2", 4 | "description": "Quickly preview an artboard in your browser.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/lukasoppermann/browser-preview.git" 8 | }, 9 | "engines": { 10 | "sketch": ">=3.0" 11 | }, 12 | "skpm": { 13 | "name": "browser-preview", 14 | "manifest": "src/manifest.json", 15 | "main": "browser-preview.sketchplugin", 16 | "assets": [ 17 | "assets/**/*" 18 | ] 19 | }, 20 | "scripts": { 21 | "build": "skpm-build", 22 | "watch": "skpm-build --watch", 23 | "start": "skpm-build --watch --run", 24 | "postinstall": "npm run build && skpm-link" 25 | }, 26 | "devDependencies": { 27 | "@skpm/builder": "^0.7.7" 28 | }, 29 | "author": "Lukas Oppermann ", 30 | "dependencies": { 31 | "@skpm/fs": "^0.2.6", 32 | "sketch-module-web-view": "^3.4.3", 33 | "skpm": "^1.3.1" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /resources/settingsView.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 116 | 117 | 118 | 119 |
120 |
121 | 122 | 128 |

Choose which browser should be used to preview your designs.

129 |
130 |
131 |
132 | 133 |

Play a sound effect before the design is previewed.

134 |
135 |
136 | 137 |
138 |
139 | 140 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /src/browser-preview-settings.js: -------------------------------------------------------------------------------- 1 | const BrowserWindow = require('sketch-module-web-view'); 2 | const sketch = require('sketch'); 3 | 4 | const Settings = sketch.Settings; 5 | const UI = sketch.UI; 6 | 7 | const settingsKeys = { 8 | BROWSERPREFERENCE: 'browserPreference', 9 | SOUNDPREFERENCE: 'soundPreference' 10 | }; 11 | 12 | export default function (context) { 13 | const options = { 14 | identifier: 'browserPreviewSettings', 15 | width: 320, 16 | height: 290, 17 | show: false, 18 | resizable: false, 19 | title: 'Browser Preview - Settings', 20 | minimizable: false, 21 | maximizable: false, 22 | alwaysOnTop: false, 23 | devTools: false, 24 | center: true, 25 | backgroundColor: '#ececec', 26 | hidesOnDeactive: false 27 | }; 28 | 29 | let browserWindow = new BrowserWindow(options); 30 | 31 | browserWindow.once('ready-to-show', () => { 32 | browserWindow.show(); 33 | }); 34 | 35 | const webContents = browserWindow.webContents; 36 | 37 | webContents.on('did-start-loading', () => { 38 | let defaultSettings = JSON.stringify(getDefaultSettings()); 39 | 40 | browserWindow.webContents 41 | .executeJavaScript( 42 | "window.settings =" + defaultSettings + "; populateSettings();" 43 | ).then(res => console.info(res)) 44 | .catch(err => console.error(err)); 45 | }); 46 | 47 | webContents.on('updateSettings', data => { 48 | setSettings(data); 49 | browserWindow.close(); 50 | }); 51 | 52 | browserWindow.on('closed', () => { 53 | browserWindow = null; 54 | }); 55 | 56 | browserWindow.loadURL(require('../resources/settingsView.html')); 57 | } 58 | 59 | function getSettings() { 60 | let obj = {}; 61 | let isUndefined = true; 62 | 63 | Object.keys(settingsKeys).forEach(key => { 64 | obj[settingsKeys[key]] = Settings.settingForKey(settingsKeys[key]); 65 | isUndefined = obj[settingsKeys[key]] === undefined; 66 | }); 67 | 68 | return { 69 | data: obj, 70 | isUndefined: isUndefined 71 | }; 72 | } 73 | 74 | function setSettings(data) { 75 | Object.keys(data).forEach(key => { 76 | Settings.setSettingForKey(key, data[key]); 77 | }); 78 | } 79 | 80 | export function getDefaultSettings() { 81 | const currentSettings = getSettings(); 82 | 83 | if (currentSettings.isUndefined) { 84 | let obj = {}; 85 | (obj[settingsKeys.BROWSERPREFERENCE] = 'safari'), 86 | (obj[settingsKeys.SOUNDPREFERENCE] = false); 87 | setSettings(obj); 88 | return obj; 89 | } else { 90 | return currentSettings.data; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/browser-preview.js: -------------------------------------------------------------------------------- 1 | import * as util from './sketch-utils' 2 | import createPreview from './create-preview' 3 | 4 | var Settings = require('sketch/settings') 5 | var sketch = require('sketch') 6 | 7 | export default function (context) { 8 | // export options 9 | const options = { 10 | scales: '2', 11 | formats: 'png', 12 | output: '/tmp', 13 | overwriting: true 14 | } 15 | let browser = Settings.settingForKey('browserPreference') || 'Default'; 16 | let sound = Settings.settingForKey('soundPreference'); 17 | 18 | // get sketch document 19 | const document = sketch.getSelectedDocument() 20 | // get selected page 21 | // const page = document.selectedPage 22 | const layers = document.selectedLayers 23 | let artboards = [] 24 | let artboardIds = [] 25 | // if no artboard selected 26 | if (layers.length === 0) { 27 | context.document.showMessage('⚠️ Please select at least one artboard or layer.'); 28 | return; 29 | } 30 | 31 | if (layers.length >= 1) { 32 | artboards = layers.layers.map(layer => { 33 | // get parent Artboard if layer is not an artboard 34 | if (layer.getParentArtboard() !== undefined) { 35 | layer = layer.getParentArtboard() 36 | } 37 | // return special artboard object 38 | if (layer.type === 'Artboard') { 39 | return { 40 | name: encodeURIComponent(layer.name), 41 | id: layer.id, 42 | backgroundColor: layer.background.color, 43 | bounds: layer.frame, 44 | object: layer 45 | } 46 | } 47 | }) 48 | .filter(artboard => { 49 | if (artboardIds.indexOf(artboard.id) > -1) { 50 | return false 51 | } 52 | return artboardIds.push(artboard.id) 53 | }) 54 | 55 | context.document.showMessage(`Creating preview for ${artboards.length} artboards.`) 56 | 57 | artboards.forEach(artboard => { 58 | let previewFile = createPreview(artboard, options) 59 | // play sound if the setting is enabled 60 | if (sound) { 61 | util.runCommand("/usr/bin/afplay", ["/System/Library/Sounds/Glass.aiff"]) 62 | } 63 | // open export in browser 64 | if (browser === 'Default') { 65 | util.runCommand('/usr/bin/open', [previewFile]) 66 | } else { 67 | util.runCommand('/usr/bin/open', ["-a", browser, previewFile]) 68 | } 69 | }); 70 | 71 | // if( layer.type === 'Artboard' ) { 72 | // let previewFile = createPreview(artboard, options) 73 | // // play sound 74 | // util.runCommand("/usr/bin/afplay", ["/System/Library/Sounds/Glass.aiff"]) 75 | // // open export in browser 76 | // if (browser === 'Default') { 77 | // util.runCommand('/usr/bin/open', [previewFile]) 78 | // } else { 79 | // util.runCommand('/usr/bin/open', ["-a", browser, previewFile]) 80 | // } 81 | // } 82 | // ) 83 | 84 | // const artboard = context.selection.firstObject(); 85 | // if( artboard && artboard.isKindOfClass(MSArtboardGroup) ){ 86 | // // show message 87 | // context.document.showMessage(`Creating preview for "${artboard.name()}" in ${browser}`) 88 | // // create export file directory 89 | // let previewFile = createPreview(artboard, options) 90 | // // play sound 91 | // util.runCommand("/usr/bin/afplay", ["/System/Library/Sounds/Glass.aiff"]) 92 | // // open export in browser 93 | // if (browser === 'Default') { 94 | // util.runCommand('/usr/bin/open', [previewFile]) 95 | // } else { 96 | // util.runCommand('/usr/bin/open', ["-a", browser, previewFile]) 97 | // } 98 | // } 99 | return; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/create-preview.js: -------------------------------------------------------------------------------- 1 | let sketch = require('sketch') 2 | let fs = require('@skpm/fs') 3 | 4 | export default (artboard 5 | /* = { 6 | object: artboard, 7 | name: string 8 | backgroundColor: string, 9 | bounds: { 10 | width: px 11 | height: px 12 | } 13 | }*/ 14 | , options) => { 15 | // export file 16 | sketch.export(artboard.object, options) 17 | let artboardFileName = artboard.name.replace(/%2F/gi, '/').replace(/%252F/gi, '/') 18 | let file = options.output + "/" + artboardFileName + "@" + options.scales + "x." + options.formats 19 | let htmlFile = `${options.output}/${artboard.name}.html` 20 | let align = artboard.name.split(':').pop().trim() 21 | 22 | // create html 23 | let html = ` 24 | 25 | 26 | ${decodeURI(artboard.name)} 27 | 45 | 46 | 47 |
48 | ${artboard.name} 49 |
50 | 51 | ` 52 | // create file 53 | fs.writeFileSync(htmlFile, html) 54 | // return file 55 | return htmlFile 56 | } 57 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "compatibleVersion": 3, 3 | "bundleVersion": 1, 4 | "identifier": "com.lukasoppermann.sketch.browser-preview", 5 | "name": "🔍 Browser Preview", 6 | "description": "Quickly preview an artboard in your browser.", 7 | "author": "Lukas Oppermann", 8 | "icon": "browser-preview.png", 9 | "commands": [ 10 | { 11 | "name": "Preview Artboard", 12 | "identifier": "browser-preview", 13 | "script": "./browser-preview.js", 14 | "shortcut": "cmd shift p" 15 | }, 16 | { 17 | "name": "Settings", 18 | "identifier": "browser-preview-settings", 19 | "script": "./browser-preview-settings.js" 20 | } 21 | ], 22 | "menu": { 23 | "title": "🔍 Browser Preview", 24 | "items": [ 25 | "browser-preview", 26 | "browser-preview-settings" 27 | ] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/sketch-utils.js: -------------------------------------------------------------------------------- 1 | export function runCommand(command, args) { 2 | var task = NSTask.alloc().init(); 3 | task.setLaunchPath_(command); 4 | task.arguments = args; 5 | task.launch(); 6 | task.waitUntilExit(); 7 | return (task.terminationStatus() == 0) 8 | } 9 | --------------------------------------------------------------------------------