├── .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 | 
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 | \n
\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
\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 |
Prefered Browser:
122 |
123 | Safari
124 | Chrome
125 | Firefox
126 | Opera
127 |
128 |
Choose which browser should be used to preview your designs.
129 |
130 |
131 |
132 |
Play Sound:
133 |
Play a sound effect before the design is previewed.
134 |
135 |
136 | Save settings
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 |
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 |
--------------------------------------------------------------------------------