Created
May 6, 2019 07:24
-
-
Save Means88/6232c74045e78ea233e3b705eb0fad7a to your computer and use it in GitHub Desktop.
zeplin.sketchplugin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var onRun = function (context) { | |
var doc = context.document; | |
if (![doc fileURL] || [doc isDraft]) { | |
[NSApp displayDialog:@"Please save the document before exporting to Zeplin." withTitle:@"Document not saved"]; | |
return; | |
} | |
if ([doc isDocumentEdited]) { | |
var alert = [NSAlert alertWithMessageText:@"Document not saved" defaultButton:@"Save and Continue" alternateButton:@"Cancel" otherButton:@"Continue" informativeTextWithFormat:@"To capture the latest changes in this Sketch document, Zeplin needs to save it first.\n\n☝️ This might take a bit, depending on the document size."]; | |
var response = [alert runModal]; | |
if (response == NSAlertDefaultReturn) { | |
[doc showMessage:@"Saving document…"]; | |
[doc saveDocument:nil]; | |
while ([doc isDocumentEdited]) { | |
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; | |
} | |
} else if (response == NSAlertAlternateReturn) { | |
return; | |
} | |
response = nil; | |
alert = nil; | |
} | |
var foreignSymbolsUpToDate = true; | |
// `MSBadgeController` is defined on Sketch 44, `activeWindowBadgingActions` is defined on Sketch 46. | |
try { | |
var activeActions = [[doc badgeController] activeWindowBadgingActions]; | |
var activeActionsLoop = [activeActions objectEnumerator]; | |
var action = nil; | |
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; | |
} | |
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; | |
} | |
var artboardIds = [artboards valueForKeyPath:@"objectID"]; | |
var layers = [[[doc documentData] allSymbols] arrayByAddingObjectsFromArray:artboards]; | |
var pageIds = [layers valueForKeyPath:@"@distinctUnionOfObjects.parentPage.objectID"]; | |
layers = nil; | |
var uniqueArtboardSizes = []; | |
// `size` on `CGRect` fails on Mocha, on macOS 10.13, Sketch 45 and below. | |
try { | |
var loop = [artboards objectEnumerator]; | |
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, | |
height: artboardSize.height | |
}); | |
} | |
artboardSize = nil; | |
isUnique = nil; | |
} | |
artboard = nil; | |
loop = nil; | |
} catch (error) { | |
log("Unique artboard sizes failed with error “" + error + "”."); | |
} | |
artboards = nil; | |
var format = @"json"; | |
var readerClass = NSClassFromString(@"MSDocumentReader"); | |
var jsonReaderClass = NSClassFromString(@"MSDocumentZippedJSONReader"); | |
if (!readerClass || !jsonReaderClass || ![[readerClass readerForDocumentAtURL:[doc fileURL]] isKindOfClass:jsonReaderClass]) { | |
format = @"legacy"; | |
} | |
jsonReaderClass = nil; | |
readerClass = nil; | |
var assetLibraries = []; | |
// `MSAssetLibraryController` defined on Sketch 47. | |
try { | |
var assetLibrariesLoop = [[[[AppController sharedInstance] librariesController] libraries] objectEnumerator]; | |
var assetLibrary = nil; | |
while (assetLibrary = [assetLibrariesLoop nextObject]) { | |
if (![assetLibrary enabled]) { | |
continue; | |
} | |
var libraryID = [assetLibrary libraryID]; | |
if (!libraryID) { | |
continue; | |
} | |
var url = [assetLibrary locationOnDisk]; | |
if (!url) { | |
continue; | |
} | |
assetLibraries.push({ | |
id: libraryID, | |
path: [url path] | |
}); | |
} | |
assetLibrary = nil; | |
assetLibrariesLoop = nil; | |
} catch (error) { | |
log("Asset library paths by identifier failed with error “" + error + "”."); | |
} | |
var artboardNamesByIdentifier = {}; | |
var allArtboardsLoop = [[doc valueForKeyPath:@"pages.@distinctUnionOfArrays.artboards"] objectEnumerator]; | |
var artboard = nil; | |
while (artboard = [allArtboardsLoop nextObject]) { | |
artboardNamesByIdentifier[artboard.objectID()] = artboard.name(); | |
} | |
artboard = nil; | |
allArtboardsLoop = nil; | |
var name = [[[NSUUID UUID] UUIDString] stringByAppendingPathExtension:@"zpl"]; | |
var temporaryDirectory = NSTemporaryDirectory(); | |
var path = [temporaryDirectory stringByAppendingPathComponent:name]; | |
temporaryDirectory = nil; | |
name = nil; | |
var version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; | |
var sketchtoolPath = [[NSBundle mainBundle] pathForResource:@"sketchtool" ofType:nil inDirectory:@"sketchtool/bin"]; | |
var sketchmigratePath = [[NSBundle mainBundle] pathForResource:@"sketchmigrate" ofType:nil inDirectory:@"sketchtool/bin"]; | |
var directives = [NSMutableDictionary dictionary]; | |
[directives setObject:[[doc fileURL] path] forKey:@"path"]; | |
[directives setObject:artboardIds forKey:@"artboardIds"]; | |
[directives setObject:pageIds forKey:@"pageIds"]; | |
[directives setObject:format forKey:@"format"]; | |
[directives setObject:uniqueArtboardSizes forKey:@"artboardSizes"]; | |
[directives setObject:assetLibraries forKey:@"assetLibraries"]; | |
[directives setObject:artboardNamesByIdentifier forKey:@"artboardNames"]; | |
if (version) { | |
[directives setObject:version forKey:@"version"]; | |
} | |
if (sketchtoolPath) { | |
[directives setObject:sketchtoolPath forKey:@"sketchtoolPath"]; | |
} | |
if (sketchmigratePath) { | |
[directives setObject:sketchmigratePath forKey:@"sketchmigratePath"]; | |
} | |
version = nil; | |
sketchmigratePath = nil; | |
sketchtoolPath = nil; | |
artboardNames = nil; | |
assetLibraries = nil; | |
format = nil; | |
uniqueArtboardSizes = nil; | |
pageIds = nil; | |
artboardIds = nil; | |
[directives writeToFile:path atomically:false]; | |
directives = nil; | |
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; | |
applicationPath = nil; | |
path = nil; | |
} | |
var shortcutHelp = function (context) { | |
var alert = [NSAlert alertWithMessageText:@"Shortcut changed to ⌃⌘E" defaultButton:@"Continue and Export" alternateButton:@"Cancel" otherButton:nil informativeTextWithFormat:@"Due to recent shortcuts tweaks on Sketch, Zeplin's “Export Selected Artboards…” shortcut is now ⌃⌘E.\n\n☝️ That's Command, Control, E."]; | |
if ([alert runModal] == NSAlertDefaultReturn) { | |
onRun(context); | |
} | |
alert = nil; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "Zeplin", | |
"description": "Export artboards to a Zeplin project. 🚀", | |
"author": "Zeplin, Inc.", | |
"authorEmail": "dev@zeplin.io", | |
"homepage": "https://zeplin.io", | |
"version": "1.6.4", | |
"identifier": "io.zeplin.sketch-plugin", | |
"commands": [{ | |
"name": "Export Selected Artboards…", | |
"identifier": "export", | |
"shortcut": "cmd ctrl e", | |
"script": "export.cocoascript", | |
"icon": "Icons/icZeplin.png", | |
"description": "Export selected artboards to a Zeplin project. 🚀" | |
}, { | |
"name": "Exclude Sublayers", | |
"identifier": "exclude-sublayers", | |
"shortcut": "cmd shift x", | |
"script": "utils.cocoascript", | |
"handler": "excludeSublayers", | |
"icon": "Icons/icZeplin.png", | |
"description": "Exclude sublayers of selected groups or symbols." | |
}, { | |
"name": "Include Sublayers", | |
"identifier": "include-sublayers", | |
"shortcut": "cmd shift i", | |
"script": "utils.cocoascript", | |
"handler": "includeSublayers", | |
"icon": "Icons/icZeplin.png", | |
"description": "Include sublayers of selected groups or symbols." | |
}, { | |
"name": "Shortcut Help", | |
"identifier": "shortcut-help", | |
"shortcut": "cmd e", | |
"script": "export.cocoascript", | |
"handler": "shortcutHelp", | |
"icon": "Icons/icZeplin.png", | |
"description": "Learn more about “Export Selected Artboards…” shortcut change." | |
}], | |
"menu": { | |
"items": [ | |
"export", | |
{ | |
"title": "Utilities", | |
"items": [ | |
"exclude-sublayers", | |
"include-sublayers", | |
"-", | |
"shortcut-help" | |
] | |
} | |
] | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment