├── smartboards.sketchplugin ├── Contents │ └── Sketch │ │ ├── align.js │ │ ├── align-and-rename.js │ │ ├── manifest.json │ │ ├── batch-custom-rename.js │ │ └── common.js └── appcast.xml └── README.md /smartboards.sketchplugin/Contents/Sketch/align.js: -------------------------------------------------------------------------------- 1 | // import common code 2 | @import "common.js" 3 | 4 | var onRun = function(context) { 5 | var shouldRename = false; 6 | align(context, shouldRename); 7 | var doc = context.document; 8 | [doc showMessage: 'Smartboards: Artboards aligned.']; 9 | }; 10 | -------------------------------------------------------------------------------- /smartboards.sketchplugin/Contents/Sketch/align-and-rename.js: -------------------------------------------------------------------------------- 1 | // import common code 2 | @import "common.js" 3 | 4 | var onRun = function(context) { 5 | var shouldRename = true; 6 | align(context, shouldRename); 7 | var doc = context.document; 8 | [doc showMessage: 'Smartboards: Artboards aligned and renamed.']; 9 | }; 10 | -------------------------------------------------------------------------------- /smartboards.sketchplugin/appcast.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Smartboards Sketch Plugin 5 | http://sparkle-project.org/files/sparkletestcast.xml 6 | Smartboards Sketch Plugin 7 | en 8 | 9 | Version 1.1 10 | 11 | 13 |
  • Support for mixed size artboards.
  • 14 | 15 | ]]> 16 |
    17 | 18 |
    19 |
    20 |
    21 | -------------------------------------------------------------------------------- /smartboards.sketchplugin/Contents/Sketch/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "author" : "Eli Horne", 3 | "description": "Plugin to make managing and exporting artboards easier in Sketch", 4 | "appcast": "https://raw.githubusercontent.com/elihorne/smartboards/master/appcast.xml", 5 | "version": "1.1", 6 | "commands" : [ 7 | { 8 | "script" : "align.js", 9 | "handler" : "onRun", 10 | "shortcut" : "shift cmd alt G", 11 | "name" : "Align", 12 | "identifier" : "align" 13 | }, 14 | { 15 | "script" : "align-and-rename.js", 16 | "handler" : "onRun", 17 | "shortcut" : "cmd alt G", 18 | "name" : "Align and Number", 19 | "identifier" : "align-and-rename" 20 | }, 21 | { 22 | "script" : "batch-custom-rename.js", 23 | "handler" : "onRun", 24 | "shortcut" : "cmd alt R", 25 | "name" : "Rename Page Artboards", 26 | "identifier" : "customRename" 27 | } 28 | ], 29 | "identifier" : "com.elihorne.smartboards", 30 | "version" : "1.3", 31 | "homepage" : "https://github.com/elihorne/smartboards", 32 | "description" : "Align and rename artboards for easy organization.", 33 | "authorEmail" : "elihorne@gmail.com", 34 | "name" : "Smartboards" 35 | } 36 | -------------------------------------------------------------------------------- /smartboards.sketchplugin/Contents/Sketch/batch-custom-rename.js: -------------------------------------------------------------------------------- 1 | // import common code 2 | @import "common.js" 3 | 4 | var onRun = function(context) { 5 | 6 | function createDialog() { 7 | 8 | var alert = COSAlertWindow.new(); 9 | 10 | function createTextFieldWithLabel(label,defaultValue) { 11 | alert.addTextLabelWithValue(label); 12 | alert.addTextFieldWithValue(defaultValue); 13 | } 14 | 15 | alert.setMessageText("Rename Page Artboards"); 16 | alert.setInformativeText("This will replace the existing names for all artboards on this page. Leave the field empty to remove custom names."); 17 | 18 | // Name 19 | createTextFieldWithLabel("Name:",""); 20 | 21 | // Actions buttons. 22 | alert.addButtonWithTitle('OK'); 23 | alert.addButtonWithTitle('Cancel'); 24 | 25 | return alert; 26 | } 27 | 28 | var alert = createDialog(); 29 | var result = alert.runModal(); 30 | if (result == 1000) { 31 | 32 | var doc = context.document; 33 | var artboards = [[doc currentPage] artboards]; 34 | 35 | for (var i = 0; i < artboards.count(); i++) { 36 | var artboardObject = artboards[i]; 37 | 38 | var artboardName = artboardObject.name(); 39 | var customRename = alert.viewAtIndex(1).stringValue(); 40 | 41 | 42 | if(artboardPrefixRegex.test(artboardName)){ 43 | var prefix = artboardName.split('_')[0]; 44 | if(customRename.length() === 0) { 45 | log('empty string'); 46 | artboardName = prefix; 47 | } else { 48 | artboardName = prefix + '_' + customRename; 49 | } 50 | } else { 51 | artboardName = alert.viewAtIndex(1).stringValue(); 52 | } 53 | renameArtboard(artboardObject, artboardName); 54 | } 55 | } else { 56 | return; 57 | } 58 | 59 | }; 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Smartboards aims to make managing and exporting artboards easier in Sketch. 2 | > Thanks to [xsaddi](https://github.com/xsaddi) for mixed artboard size support. 3 | 4 | # Installation 5 | 6 | #### Easiest Method 7 | Use the terrific [Sketch Toolbox](http://sketchtoolbox.com/). Just search for `Smartboards` and click "`Install`" 8 | 9 | #### Slightly Harder 10 | Download `Smartboards` [as a zip file](https://github.com/elihorne/smartboards/archive/master.zip) and then add the folder to your Sketch plugin folder. 11 | 12 | How to find your Sketch plugin folder: (In Sketch 3) choose `Plugins` > `Manage Plugins...` then click the cog icon near the bottom left corner, and select `Show Plugins Folder`. 13 | 14 | # Features 15 | 16 | #### Align and Rename 17 | > Shortcut: cmd + opt + g 18 | 19 | * Aligns artboards to a grid with `100px` gutters 20 | * Renames the artboards based on column (number) and row (letter) [`A00`, `A01`, `B00`, `B01`, `B02`] 21 | * Organizes the artboards in the layers list by row and column. 22 | 23 | ![Screenshot](http://f.cl.ly/items/0d3o2p1Q3R2W2l0c3O25/smartboards.gif) 24 | 25 | Notes: `Align` expects that artboards that are meant to be in the same row are top aligned on the same Y-axis value. It doesn't care about the X-axis value. 26 | 27 | There's a slightly hidden feature that I use a lot but is hard to describe. If you place an artboard in between two rows on the Y-axis and run `Align`, it will create room for a new row by shifting existing rows down by one, and then will rename accordingly. Imagine you have `Artboard 1` that is `10x10` at `Y = 1`, and `Artboard 2` that is `10x10` at `Y = 111`. If you place a third artboard where `1 < Y < 111`, `Align` will do the rest of the heavy lifting for you. 28 | 29 | ![Screenshot](http://f.cl.ly/items/3P2p1M002x3G0W3z1h1S/smartboards-2.gif) 30 | 31 | #### Align 32 | >Shortcut: shift + cmd + opt + g 33 | 34 | Aligns artboard to the same grid, but does not rename the artboards. Use this if you don't want your artboard names messed with. `Align and Rename` does its best to preserve custom artboard names, but sometimes you might not want it. 35 | 36 | #### Rename Page Artboards 37 | 38 | Allows you to specify a custom name for all artboards. If you have Column and Row indicators (ex: A00), these will be preserved. 39 | 40 | If you'd like to remove the custom naming from all artboards, just leave the input field empty. 41 | -------------------------------------------------------------------------------- /smartboards.sketchplugin/Contents/Sketch/common.js: -------------------------------------------------------------------------------- 1 | var artboardPrefixRegex = /^[A-Z]{1,2}\d{2,3}\_/; 2 | var artboardPrefixOnly = /^[A-Z]{1,2}\d{2,3}$/; 3 | 4 | var renameArtboard = function(artboardObject, finalName) { 5 | 6 | [artboardObject setName:finalName]; 7 | } 8 | 9 | var align = function(context, shouldRename) { 10 | 11 | // Organize artboards by row and column and rename for alphabetical sort. 12 | 13 | var doc = context.document; 14 | 15 | // options 16 | var PADDING = 100; 17 | 18 | // Get all the artboards on the current page. 19 | var artboards = [[doc currentPage] artboards]; 20 | var page = [doc currentPage]; 21 | 22 | // Variables to figure out the names (letter for row, number for column). 23 | var currentRow = -1; 24 | var currentColumn = -1; 25 | var lastTop = -10000000000000000000; 26 | var rowHeight = 0; 27 | var lastWidth = 0; 28 | var lastX = 0; 29 | var lastY = 0; 30 | 31 | // Set up the artboards object. 32 | var artboardsMeta = []; 33 | 34 | // Add artboard data to the object. 35 | for (var i = 0; i < artboards.count(); i++) { 36 | var artboard = artboards[i]; 37 | var frame = artboard.frame(); 38 | var artboardName = artboard.name(); 39 | // run the name through the regex. If it matches, split and replace name with split result 40 | 41 | if(artboardPrefixRegex.test(artboardName)){ 42 | artboardName = artboardName.split(artboardPrefixRegex)[1] 43 | }; 44 | 45 | artboardsMeta.push({ 46 | artboard: artboard, 47 | name: artboardName, 48 | width: frame.width(), 49 | height: frame.height(), 50 | left: frame.x(), 51 | top: frame.y() 52 | }); 53 | } 54 | 55 | // Sort artboards by x and y position (grouping). 56 | artboardsMeta.sort(function(a, b) { 57 | if (a.top != b.top) { 58 | return a.top - b.top; 59 | } 60 | return a.left - b.left; 61 | }); 62 | 63 | // artboard object with 64 | var artboardRows = []; 65 | 66 | // Align artboards to grid, assign names. 67 | for (var i = 0; i < artboardsMeta.length; ++i) { 68 | var obj = artboardsMeta[i]; 69 | var artboard = obj.artboard; 70 | var height = obj.height; 71 | var width = obj.width; 72 | var name = obj.name; 73 | var newRow = 0; 74 | var oLeft = obj.left; 75 | var oTop = obj.top; 76 | 77 | var artboard = artboardsMeta[i]; 78 | if (artboard.top > lastTop) { 79 | ++currentRow; 80 | currentColumn = 0; 81 | lastTop = artboard.top; 82 | newRow = 1; 83 | } 84 | 85 | if(shouldRename) { 86 | // Get the letter for the row 87 | if(currentRow <= 25) { 88 | var charCode = String.fromCharCode(65 + currentRow); 89 | } 90 | 91 | if(currentRow > 25) { 92 | var charCode = 'A' + String.fromCharCode(65 + parseInt(currentRow - 26)); 93 | } 94 | 95 | // Get the zero based number for the column 96 | var formattedRow = currentColumn < 10 ? '0' + currentColumn : currentColumn; 97 | 98 | 99 | // Assemble the new artboard name -- test if name matches a generated prefix 100 | if(artboardPrefixOnly.test(name)){ 101 | //if it does, just overwrite 102 | var finalName = charCode + formattedRow; 103 | } else { 104 | //otherwise add prefix to current name 105 | var finalName = charCode + formattedRow + '_' + name; 106 | }; 107 | 108 | // finalName = lastY + "_" + rowHeight; 109 | 110 | artboardObject = artboard.artboard; 111 | renameArtboard(artboardObject, finalName); 112 | } 113 | 114 | // 115 | // LAYER POSITIONING 116 | // 117 | 118 | var artboardInternal = artboard.artboard; 119 | var frame = [artboardInternal frame]; 120 | [frame setX: (currentColumn ? lastX + lastWidth + PADDING : 0)]; 121 | [frame setY: (currentRow ? (newRow ? lastY + rowHeight + PADDING : lastY):0)]; 122 | 123 | if ((height>rowHeight) || (newRow)) { 124 | rowHeight = height; 125 | } 126 | 127 | lastWidth = width; 128 | lastX = artboardInternal.frame().x(); 129 | lastY = artboardInternal.frame().y(); 130 | 131 | ++currentColumn; 132 | } 133 | 134 | // 135 | // LAYER ORDERING 136 | // 137 | 138 | // Sort artboards in the Layers List. 139 | var layersList = []; 140 | for (var i = 0; i < artboards.count(); i++) { 141 | var artboard = artboards[i]; 142 | var name = artboard.name(); 143 | layersList.push({ 144 | artboard: artboard, 145 | name: name 146 | }); 147 | } 148 | 149 | // Sort layers list by name. 150 | layersList.sort(function(a, b){ 151 | if(a.name < b.name) { 152 | return 1 153 | } 154 | if(a.name > b.name) { 155 | return -1 156 | } 157 | return 0 158 | }); 159 | 160 | // Helper function to add artboards. 161 | function addArtboard(page, artboard) { 162 | var frame = artboard.frame(); 163 | frame.constrainProportions = false; 164 | page.addLayers([artboard]); 165 | return artboard; 166 | }; 167 | 168 | // Helper function to remove artboards. 169 | function removeArtboardFromPage(page, name) { 170 | var theArtboard = null; 171 | var abs = page.artboards().objectEnumerator(); 172 | 173 | while (a = abs.nextObject()) { 174 | if (a.name() == name) { 175 | page.removeLayer(a) 176 | break; 177 | } 178 | } 179 | } 180 | 181 | // Loop through the alphabetized items, remove the old ones and add the new ones. 182 | for (var i = 0; i < layersList.length; i++) { 183 | var artboard = layersList[i]; 184 | removeArtboardFromPage(page, artboard.name) 185 | addArtboard(page, artboard.artboard); 186 | } 187 | 188 | }; 189 | --------------------------------------------------------------------------------