diff --git a/Zeplin.sketchplugin/Contents/Sketch/main.cocoascript b/Zeplin.sketchplugin/Contents/Sketch/main.cocoascript index b668115..e90fc0a 100644 --- a/Zeplin.sketchplugin/Contents/Sketch/main.cocoascript +++ b/Zeplin.sketchplugin/Contents/Sketch/main.cocoascript @@ -1,17 +1,21 @@ @import "utils.cocoascript"; var onRun = function (context) { - var temporaryPath = temporaryPathForDesignFile(context); - if (!temporaryPath) { - return; - } - var artboards = [context valueForKeyPath:@"selection.@distinctUnionOfObjects.parentArtboard"]; if (![artboards count]) { [NSApp displayDialog:@"Please select the artboards you want to export to Zeplin.\n\n☝️ Selecting a layer inside the artboard should be enough." withTitle:@"No artboard selected"]; return; } + if (!isSelectionExportable(artboards)) { + return; + } + + var temporaryPath = temporaryPathForDesignFile(context); + if (!temporaryPath) { + return; + } + exportArtboards(context, artboards, temporaryPath); } @@ -34,70 +38,82 @@ var exportTextStyles = function (context) { } var exportArtboardsFromCurrentPage = function (context) { - var temporaryPath = temporaryPathForDesignFile(context); - if (!temporaryPath) { - return; - } - var artboards = layersOfPagesWithClassName(NSArray.arrayWithObject(context.document.currentPage()), "MSArtboardGroup"); - if (![artboards count]) { [NSApp displayDialog:@"Please create an artboard to export to Zeplin." withTitle:@"No artboard found"]; return; } - exportArtboards(context, artboards, temporaryPath); -} + if (!isSelectionExportable(artboards)) { + return; + } -var exportArtboardsFromAllPages = function (context) { var temporaryPath = temporaryPathForDesignFile(context); if (!temporaryPath) { return; } - var artboards = layersOfPagesWithClassName(context.document.pages(), "MSArtboardGroup"); + exportArtboards(context, artboards, temporaryPath); +} +var exportArtboardsFromAllPages = function (context) { + var artboards = layersOfPagesWithClassName(context.document.pages(), "MSArtboardGroup"); if (![artboards count]) { [NSApp displayDialog:@"Please create an artboard to export to Zeplin." withTitle:@"No artboard found"]; return; } - exportArtboards(context, artboards, temporaryPath); -} + if (!isSelectionExportable(artboards)) { + return; + } -var exportSymbolsFromCurrentPage = function (context) { var temporaryPath = temporaryPathForDesignFile(context); if (!temporaryPath) { return; } - var artboards = layersOfPagesWithClassName(NSArray.arrayWithObject(context.document.currentPage()), "MSSymbolMaster"); + exportArtboards(context, artboards, temporaryPath); +} +var exportSymbolsFromCurrentPage = function (context) { + var artboards = layersOfPagesWithClassName(NSArray.arrayWithObject(context.document.currentPage()), "MSSymbolMaster"); if (![artboards count]) { [NSApp displayDialog:@"Please create a symbol to export to Zeplin." withTitle:@"No symbol found"]; return; } - exportArtboards(context, artboards, temporaryPath); -} + if (!isSelectionExportable(artboards)) { + return; + } -var exportSymbolsFromAllPages = function (context) { var temporaryPath = temporaryPathForDesignFile(context); if (!temporaryPath) { return; } - var artboards = layersOfPagesWithClassName(context.document.pages(), "MSSymbolMaster"); + exportArtboards(context, artboards, temporaryPath); +} +var exportSymbolsFromAllPages = function (context) { + var artboards = layersOfPagesWithClassName(context.document.pages(), "MSSymbolMaster"); if (![artboards count]) { [NSApp displayDialog:@"Please create a symbol to export to Zeplin." withTitle:@"No symbol found"]; return; } + if (!isSelectionExportable(artboards)) { + return; + } + + var temporaryPath = temporaryPathForDesignFile(context); + if (!temporaryPath) { + return; + } + exportArtboards(context, artboards, temporaryPath); } @@ -105,17 +121,17 @@ var excludeSublayers = function (context) { var selection = context.selection; var layerEnumerator = [selection objectEnumerator]; var layer; - + while (layer = [layerEnumerator nextObject]) { var layerName = [layer name]; - + if (![layerName hasPrefix:@"-g-"]) { [layer setName:[@"-g-" stringByAppendingString:layerName]]; } - + layerName = nil; } - + layer = nil; layerEnumerator = nil; selection = nil; @@ -125,17 +141,17 @@ var includeSublayers = function (context) { var selection = context.selection; var layerEnumerator = [selection objectEnumerator]; var layer; - + while (layer = [layerEnumerator nextObject]) { var layerName = [layer name]; - + if ([layerName hasPrefix:@"-g-"]) { [layer setName:[layerName substringFromIndex:3]]; } - + layerName = nil; } - + layer = nil; layerEnumerator = nil; selection = nil; diff --git a/Zeplin.sketchplugin/Contents/Sketch/manifest.json b/Zeplin.sketchplugin/Contents/Sketch/manifest.json index 006e5bb..7283072 100644 --- a/Zeplin.sketchplugin/Contents/Sketch/manifest.json +++ b/Zeplin.sketchplugin/Contents/Sketch/manifest.json @@ -4,7 +4,7 @@ "author": "Zeplin, Inc.", "authorEmail": "dev@zeplin.io", "homepage": "https://zeplin.io", - "version": "1.10", + "version": "1.11", "identifier": "io.zeplin.sketch-plugin", "icon": "Icons/icZeplin.png", "commands": [{ @@ -94,7 +94,7 @@ "title": "Utilities", "items": [ "exclude-sublayers", - "include-sublayers" + "include-sublayers" ] } ] diff --git a/Zeplin.sketchplugin/Contents/Sketch/utils.cocoascript b/Zeplin.sketchplugin/Contents/Sketch/utils.cocoascript index 5c4ec99..f5c6182 100644 --- a/Zeplin.sketchplugin/Contents/Sketch/utils.cocoascript +++ b/Zeplin.sketchplugin/Contents/Sketch/utils.cocoascript @@ -1,8 +1,36 @@ +var isSelectionExportable = function (artboards) { + var artboardPixelLimit = 1000000000; // 1e9 + var overSizedArtboardName = nil; + + var artboardsLoop = [artboards objectEnumerator]; + var artboard = nil; + while (artboard = [artboardsLoop nextObject]) { + var artboardSize = artboard.rect().size; + if (artboardSize.width * artboardSize.height > artboardPixelLimit) { + overSizedArtboardName = artboard.name(); + break; + } + } + + artboardSize = nil; + artboard = nil; + artboardsLoop = nil; + artboardPixelLimit = nil; + + if (overSizedArtboardName) { + [NSApp displayDialog:@"Selected artboard “" + overSizedArtboardName + "” is too large to export. Due to a limitation in Sketch, it’s only possible to export artboards smaller than 30,000 px ⨉ 30,000 px.\n\n☝️ You can divide the artboard into multiple artboards and export again." withTitle:@"Artboard too large"]; + + return false; + } + + return true; +} + var temporaryPath = function() { var name = [[[NSUUID UUID] UUIDString] stringByAppendingPathExtension:@"sketch"]; var temporaryDirectory = NSTemporaryDirectory(); var temporaryZeplinDirectory = [temporaryDirectory stringByAppendingPathComponent:@"io.zeplin.osx"]; - + var isDir = MOPointer.alloc().initWithValue_(false); var fileManager = [NSFileManager defaultManager]; if (![fileManager fileExistsAtPath:temporaryZeplinDirectory isDirectory:isDir] || isDir.value() == false) { @@ -79,7 +107,7 @@ var defaultDirectives = function(context, temporaryPath) { if (![assetLibrary enabled]) { continue; } - + var libraryID = [assetLibrary libraryID]; if (!libraryID) { continue; @@ -89,13 +117,13 @@ var defaultDirectives = function(context, temporaryPath) { if (!url) { continue; } - + assetLibraries.push({ id: libraryID, path: [url path] }); } - + assetLibrary = nil; assetLibrariesLoop = nil; } catch (error) { @@ -143,16 +171,16 @@ var writeDirectives = function (directives, path) { var launchZeplin = function (context, path) { var doc = context.document; var workspace = [NSWorkspace sharedWorkspace]; - + var applicationPath = [workspace absolutePathForAppBundleWithIdentifier:@"io.zeplin.osx"]; if (!applicationPath) { [NSApp displayDialog:@"Please make sure that you installed and launched it: https://zpl.io/download" withTitle:"Could not find Zeplin"]; return; } - + [doc showMessage:@"Launching Zeplin!"]; - + [workspace openFile:path withApplication:applicationPath andDeactivate:true]; workspace = nil; @@ -171,25 +199,25 @@ var exportArtboards = function (context, artboards, temporaryPath) { while (action = [activeActionsLoop nextObject]) { if ([action isKindOfClass:NSClassFromString(@"MSSyncLibraryAction")]) { foreignSymbolsUpToDate = false; - + break; } } - + action = nil; activeActionsLoop = nil; activeActions = nil; } catch (error) { log("Foreign symbols up to date failed with error “" + error + "”."); } - + if (!foreignSymbolsUpToDate) { var alert = [NSAlert alertWithMessageText:@"Symbols not up to date" defaultButton:@"Continue and Export" alternateButton:@"Cancel" otherButton:nil informativeTextWithFormat:@"To capture the latest changes in your libraries, make sure that your symbols are up to date before exporting artboards to Zeplin.\n\n☝️ Select “Library Update Available” on the top right to review changes."]; - + if ([alert runModal] == NSAlertAlternateReturn) { return; } - + alert = nil; } @@ -197,7 +225,7 @@ var exportArtboards = function (context, artboards, temporaryPath) { var layers = [[[doc documentData] allSymbols] arrayByAddingObjectsFromArray:artboards]; var pageIds = [layers valueForKeyPath:@"@distinctUnionOfObjects.parentPage.objectID"]; - + layers = nil; var containsArtboard = false; @@ -207,7 +235,7 @@ var exportArtboards = function (context, artboards, temporaryPath) { var artboardClassName = NSStringFromClass([artboard class]); if ([artboardClassName isEqualToString:@"MSArtboardGroup"]) { containsArtboard = true; - + break; } } @@ -222,33 +250,33 @@ var exportArtboards = function (context, artboards, temporaryPath) { var artboard = nil; while (artboard = [loop nextObject]) { var artboardSize = artboard.rect().size; - + var isUnique = true; for (var k = 0; k < uniqueArtboardSizes.length; k++) { if (uniqueArtboardSizes[k].width == artboardSize.width && uniqueArtboardSizes[k].height == artboardSize.height) { isUnique = false; - + break; } } - + if (isUnique) { uniqueArtboardSizes.push({ - width: artboardSize.width, + width: artboardSize.width, height: artboardSize.height }); } - + artboardSize = nil; isUnique = nil; } - + artboard = nil; loop = nil; } catch (error) { log("Unique artboard sizes failed with error “" + error + "”."); } - + artboards = nil; var artboardNamesByIdentifier = {}; @@ -273,7 +301,7 @@ var exportArtboards = function (context, artboards, temporaryPath) { pageIds = nil; uniqueArtboardSizes = nil; artboardNamesByIdentifier = nil; - + var path = directivesPath(); writeDirectives(directives, path); @@ -303,12 +331,12 @@ var layersOfPagesWithClassName = function (pages, className) { var pagesLoop = pages.objectEnumerator(); var page = nil; - while (page = [pagesLoop nextObject]) { + while (page = [pagesLoop nextObject]) { var pageLayers = []; var layersLoop = page.layers().objectEnumerator(); var layer = nil; - while (layer = [layersLoop nextObject]) { + while (layer = [layersLoop nextObject]) { var layerClassName = NSStringFromClass([layer class]); if ([layerClassName isEqualToString:className]) { @@ -317,7 +345,7 @@ var layersOfPagesWithClassName = function (pages, className) { } layers = layers.concat(pageLayers); - } - + } + return NSArray.arrayWithArray(layers); } diff --git a/pull_request_template.md b/pull_request_template.md new file mode 100644 index 0000000..2326f23 --- /dev/null +++ b/pull_request_template.md @@ -0,0 +1,31 @@ +## Change description + +> Description here + +## Type of change +- [ ] Bug fix (fixes an issue) +- [ ] New feature (adds functionality) + +## Related issues + +> Fix [#1]() + +## Checklists + +### Development + +- [ ] Lint rules pass locally +- [ ] Application changes have been tested thoroughly +- [ ] Automated tests covering modified code pass + +### Security + +- [ ] Security impact of change has been considered +- [ ] Code follows company security practices and guidelines + +### Code review + +- [ ] Pull request has a descriptive title and context useful to a reviewer. Screenshots or screencasts are attached as necessary +- [ ] "Ready for review" label attached and reviewers assigned +- [ ] Changes have been reviewed by at least one other contributor +- [ ] Pull request linked to task tracker where applicable