├── .gitattributes ├── LICENSE.md ├── README.md ├── Shared Style Finder.sketchplugin └── Contents │ ├── Resources │ └── icon.png │ └── Sketch │ ├── delegate.js │ ├── manifest.json │ └── script.js ├── appcast.xml └── images ├── logo.png └── screenshot.png /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jason Burns (Sonburn) 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 | ![Shared Style Finder](https://raw.githubusercontent.com/sonburn/shared-style-finder/master/images/logo.png) 2 | 3 | Find instances of a shared layer or text style. 4 | 5 | ![Shared Style Finder](https://raw.githubusercontent.com/sonburn/shared-style-finder/master/images/screenshot.png) 6 | 7 | 8 | runner-badge-blue 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | # Usage 20 | 21 | * cmd option shift f - Find instances of a shared layer or text style 22 | 23 | # Installation 24 | 25 | ## Automatic 26 | Search for Shared Style Finder in [Sketchrunner](http://sketchrunner.com/), [Sketchpacks](https://sketchpacks.com/), or [Sketch Toolbox](http://sketchtoolbox.com/) if you have one of those installed. 27 | 28 | Once installed, Sketch will automatically notify you when an update is available (version 0.1 and later). 29 | 30 | ## Manual 31 | 32 | 1. Download and open shared-style-finder-master.zip 33 | 2. Navigate to Shared Style Finder.sketchplugin and copy/move to your plugins directory 34 | 35 | To find your plugins directory... 36 | 37 | 1. In the Sketch menu, navigate to Plugins > Manage Plugins... 38 | 2. Click the cog in the lower left of the plugins window, and click Reveal Plugins Folder 39 | 40 | # Changelog 41 | 42 | * **1.2** - Fix for Sketch 53. 43 | * **1.1** - Library style solution broke Sketch 51, so library styles now only shown for Sketch 52. 44 | * **1.0** - Now includes library styles, improved support for dark mode, and a wider panel for better label viewing. 45 | * **0.7** - Added option to remove a style if it's unused. 46 | * **0.6** - Fix for Sketch 52 changes to styles. 47 | * **0.5** - General optimizations. 48 | * **0.4** - Added plugin icon to manifest for Sketch 50. 49 | * **0.3** - Fix for Sketch 48 change of currentView to contentDrawView. 50 | * **0.2** - Improved handling when no shared styles. 51 | * **0.1** - Initial commit. 52 | 53 | # Contact 54 | 55 | Find me on Twitter @sonburn 56 | 57 | # Support 58 | 59 | If you find this plugin helpful, or would like to support my plugins in general, buy me ☕️ via PayPal. 60 | 61 | # License 62 | 63 | Copyright (c) 2019 Jason Burns (Sonburn). See LICENSE.md for further details. 64 | -------------------------------------------------------------------------------- /Shared Style Finder.sketchplugin/Contents/Resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonburn/shared-style-finder/e8ab56f4af9b026f329ba471015ce8c8a2c99768/Shared Style Finder.sketchplugin/Contents/Resources/icon.png -------------------------------------------------------------------------------- /Shared Style Finder.sketchplugin/Contents/Sketch/delegate.js: -------------------------------------------------------------------------------- 1 | // 2 | // MochaJSDelegate.js 3 | // MochaJSDelegate 4 | // 5 | // Created by Matt Curtis 6 | // Copyright (c) 2015. All rights reserved. 7 | // 8 | 9 | var MochaJSDelegate = function(selectorHandlerDict){ 10 | var uniqueClassName = "MochaJSDelegate_DynamicClass_" + NSUUID.UUID().UUIDString(); 11 | 12 | var delegateClassDesc = MOClassDescription.allocateDescriptionForClassWithName_superclass_(uniqueClassName, NSObject); 13 | 14 | delegateClassDesc.registerClass(); 15 | 16 | // Handler storage 17 | 18 | var handlers = {}; 19 | 20 | // Define interface 21 | 22 | this.setHandlerForSelector = function(selectorString, func){ 23 | var handlerHasBeenSet = (selectorString in handlers); 24 | var selector = NSSelectorFromString(selectorString); 25 | 26 | handlers[selectorString] = func; 27 | 28 | if(!handlerHasBeenSet){ 29 | /* 30 | For some reason, Mocha acts weird about arguments: 31 | https://github.com/logancollins/Mocha/issues/28 32 | 33 | We have to basically create a dynamic handler with a likewise dynamic number of predefined arguments. 34 | */ 35 | 36 | var dynamicHandler = function(){ 37 | var functionToCall = handlers[selectorString]; 38 | 39 | if(!functionToCall) return; 40 | 41 | return functionToCall.apply(delegateClassDesc, arguments); 42 | }; 43 | 44 | var args = [], regex = /:/g; 45 | while(match = regex.exec(selectorString)) args.push("arg"+args.length); 46 | 47 | dynamicFunction = eval("(function("+args.join(",")+"){ return dynamicHandler.apply(this, arguments); })"); 48 | 49 | delegateClassDesc.addInstanceMethodWithSelector_function_(selector, dynamicFunction); 50 | } 51 | }; 52 | 53 | this.removeHandlerForSelector = function(selectorString){ 54 | delete handlers[selectorString]; 55 | }; 56 | 57 | this.getHandlerForSelector = function(selectorString){ 58 | return handlers[selectorString]; 59 | }; 60 | 61 | this.getAllHandlers = function(){ 62 | return handlers; 63 | }; 64 | 65 | this.getClass = function(){ 66 | return NSClassFromString(uniqueClassName); 67 | }; 68 | 69 | this.getClassInstance = function(){ 70 | return NSClassFromString(uniqueClassName).new(); 71 | }; 72 | 73 | // Conveience 74 | 75 | if(typeof selectorHandlerDict == "object"){ 76 | for(var selectorString in selectorHandlerDict){ 77 | this.setHandlerForSelector(selectorString, selectorHandlerDict[selectorString]); 78 | } 79 | } 80 | }; 81 | -------------------------------------------------------------------------------- /Shared Style Finder.sketchplugin/Contents/Sketch/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Shared Style Finder", 3 | "description" : "Find instances of a shared layer or text style.", 4 | "author" : "Jason Burns", 5 | "homepage" : "https://github.com/sonburn/shared-style-finder", 6 | "version" : "1.2", 7 | "identifier" : "com.sonburn.sketchplugins.shared-style-finder", 8 | "appcast" : "https://raw.githubusercontent.com/sonburn/shared-style-finder/master/appcast.xml", 9 | "icon" : "icon.png", 10 | "commands" : [ 11 | { 12 | "name" : "Shared Style Finder", 13 | "shortcut" : "cmd option shift f", 14 | "identifier" : "find", 15 | "description" : "Find instances of a shared layer or text style.", 16 | "icon" : "icon.png", 17 | "script" : "script.js", 18 | "handler" : "find" 19 | }, 20 | { 21 | "name" : "Report Issue", 22 | "identifier" : "report", 23 | "description" : "Report an issue with Shared Style Finder.", 24 | "icon" : "icon.png", 25 | "script" : "script.js", 26 | "handler" : "report" 27 | }, 28 | { 29 | "name" : "Other Plugins", 30 | "identifier" : "plugins", 31 | "description" : "View all of Sonburn's plugins.", 32 | "icon" : "icon.png", 33 | "script" : "script.js", 34 | "handler" : "plugins" 35 | }, 36 | { 37 | "name" : "Donate", 38 | "identifier" : "donate", 39 | "description" : "Donate to the development of Shared Style Finder.", 40 | "icon" : "icon.png", 41 | "script" : "script.js", 42 | "handler" : "donate" 43 | } 44 | ], 45 | "menu" : { 46 | "title" : "Shared Style Finder", 47 | "items" : [ 48 | "find", 49 | "-", 50 | "report", 51 | "plugins", 52 | "donate" 53 | ] 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Shared Style Finder.sketchplugin/Contents/Sketch/script.js: -------------------------------------------------------------------------------- 1 | @import "delegate.js"; 2 | 3 | var sketch = require("sketch"), 4 | pluginName = "Shared Style Finder", 5 | pluginIdentifier = "com.sonburn.sketchplugins.shared-style-finder", 6 | debugMode = false; 7 | 8 | var layerStyles = getSharedStyles(0), 9 | textStyles = getSharedStyles(1), 10 | styles, 11 | styleNames, 12 | uiButtons = []; 13 | 14 | var find = function(context) { 15 | if (!layerStyles.length && !textStyles.length) { 16 | displayDialog("This document has no shared styles.",pluginName); 17 | return; 18 | } 19 | 20 | var alertWidth = 380, 21 | contentFrameWidth = alertWidth, 22 | contentFrameGutter = 15, 23 | layerStyleWidth = contentFrameWidth - contentFrameGutter, 24 | layerStyleHeight = 96, 25 | layerStylesVisible = 4, 26 | settingPad = 10, 27 | settingY = 0; 28 | 29 | var alert = NSAlert.alloc().init(), 30 | alertIconPath = context.plugin.urlForResourceNamed("icon.png").path(), 31 | alertIcon = NSImage.alloc().initByReferencingFile(alertIconPath), 32 | alertContent = NSView.alloc().init(); 33 | 34 | alert.setIcon(alertIcon); 35 | alert.setMessageText(pluginName); 36 | alert.setInformativeText("Find instances of a shared layer or text style."); 37 | 38 | alertContent.setFlipped(true); 39 | 40 | var styleType = createSegmentedControl(["Layer Styles","Text Styles"],NSMakeRect(0,settingY,alertWidth,24)); 41 | 42 | styles = layerStyles; 43 | 44 | if (!textStyles.length) { 45 | styleType.setEnabled_forSegment(0,1); 46 | styleType.setSelected_forSegment(1,0); 47 | } else if (!layerStyles.length) { 48 | styleType.setEnabled_forSegment(0,0); 49 | styleType.setSelected_forSegment(1,1); 50 | 51 | styles = textStyles; 52 | } else { 53 | styleType.cell().setAction("callAction:"); 54 | styleType.cell().setCOSJSTargetFunction(function(sender) { 55 | styles = (sender.indexOfSelectedItem() == 0) ? layerStyles : textStyles; 56 | styleNames = getStyleNames(styles); 57 | 58 | if (styleNames) { 59 | styleList.removeAllItems(); 60 | styleList.addItemsWithObjectValues(styleNames); 61 | styleList.selectItemAtIndex(0); 62 | } 63 | }); 64 | } 65 | 66 | styleNames = getStyleNames(styles); 67 | 68 | alertContent.addSubview(styleType); 69 | 70 | settingY = CGRectGetMaxY(alertContent.subviews().lastObject().frame()) + settingPad; 71 | 72 | var styleList = createSelect(styleNames,0,NSMakeRect(0,settingY,alertWidth,28)); 73 | 74 | alertContent.addSubview(styleList); 75 | 76 | var styleListDelegate = new MochaJSDelegate({ 77 | "comboBoxSelectionDidChange:" : (function() { 78 | if (styleList.indexOfSelectedItem() != -1) { 79 | var selectedItem = styleList.indexOfSelectedItem(); 80 | 81 | var objectsWithStyleID = (sketch.version.sketch < 52) ? getObjectsWithStyleIDOld(styles[selectedItem].objectID()) : getObjectsWithStyleID(styles[selectedItem].objectID()); 82 | 83 | var subviews = layerListContent.subviews(); 84 | 85 | for (var i = subviews.length - 1; i >= 0; --i) { 86 | subviews[i].removeFromSuperview(); 87 | } 88 | 89 | var layerListCount = 0; 90 | 91 | for (var i = 0; i < objectsWithStyleID.length; i++) { 92 | layerListContent.addSubview(createListItem(objectsWithStyleID[i],NSMakeRect(0,layerStyleHeight*layerListCount,layerStyleWidth,layerStyleHeight))); 93 | 94 | layerListCount++; 95 | } 96 | 97 | layerListContent.frame = NSMakeRect(0,0,layerStyleWidth,objectsWithStyleID.length*layerStyleHeight); 98 | 99 | matchCount.setStringValue("Matches: " + objectsWithStyleID.length); 100 | 101 | if (objectsWithStyleID.length == 0) { 102 | removeStyleButton.setEnabled(1); 103 | } else { 104 | removeStyleButton.setEnabled(0); 105 | } 106 | } 107 | }) 108 | }); 109 | 110 | styleList.setDelegate(styleListDelegate.getClassInstance()); 111 | 112 | var objectsWithStyleID = (sketch.version.sketch < 52) ? getObjectsWithStyleIDOld(styles[0].objectID()) : getObjectsWithStyleID(styles[0].objectID()); 113 | 114 | settingY = CGRectGetMaxY(alertContent.subviews().lastObject().frame()) + settingPad; 115 | 116 | var layerList = createScrollView(NSMakeRect(0,settingY,contentFrameWidth,layerStyleHeight * layerStylesVisible)), 117 | layerListContent = createContentView(NSMakeRect(0,0,layerStyleWidth,objectsWithStyleID.length*layerStyleHeight)), 118 | layerListCount = 0; 119 | 120 | for (var i = 0; i < objectsWithStyleID.length; i++) { 121 | layerListContent.addSubview(createListItem(objectsWithStyleID[i],NSMakeRect(0,layerStyleHeight*layerListCount,layerStyleWidth,layerStyleHeight))); 122 | 123 | layerListCount++; 124 | } 125 | 126 | layerList.setBackgroundColor(NSColor.whiteColor()); 127 | layerList.setDocumentView(layerListContent); 128 | 129 | alertContent.addSubview(layerList); 130 | 131 | settingY = CGRectGetMaxY(alertContent.subviews().lastObject().frame()) + settingPad; 132 | 133 | var matchCount = createMatchText("Matches: " + objectsWithStyleID.length,NSMakeRect(0,settingY,alertWidth,18)); 134 | alertContent.addSubview(matchCount); 135 | 136 | alertContent.frame = NSMakeRect(0,0,alertWidth,CGRectGetMaxY(matchCount.frame())); 137 | 138 | alert.accessoryView = alertContent; 139 | 140 | var closeButton = alert.addButtonWithTitle("Close"); 141 | var removeStyleButton = alert.addButtonWithTitle("Remove Style"); 142 | 143 | if (objectsWithStyleID.length == 0) { 144 | removeStyleButton.setEnabled(1); 145 | } else { 146 | removeStyleButton.setEnabled(0); 147 | } 148 | 149 | var alertResponse = alert.runModal(); 150 | 151 | if (!debugMode) googleAnalytics(context,"find","run"); 152 | 153 | if (alertResponse == 1000) { 154 | // Do something 155 | } else if (alertResponse == 1001) { 156 | var document = sketch.getSelectedDocument(), 157 | selectedItem = styleList.indexOfSelectedItem(), 158 | selectedStyle = styles[selectedItem], 159 | selectedStyleSource; 160 | 161 | if (document.getSharedLayerStyleWithID(selectedStyle.objectID())) { 162 | selectedStyle = document.getSharedLayerStyleWithID(selectedStyle.objectID()); 163 | selectedStyleSource = MSDocument.currentDocument().documentData().layerStyles(); 164 | } else { 165 | selectedStyle = document.getSharedTextStyleWithID(selectedStyle.objectID()); 166 | selectedStyleSource = MSDocument.currentDocument().documentData().layerTextStyles(); 167 | } 168 | 169 | if (!selectedStyle.getAllInstancesLayers().length) { 170 | if (selectedStyleSource.sharedStyleWithID) { 171 | selectedStyleSource.removeSharedStyle(selectedStyleSource.sharedStyleWithID(selectedStyle.id)); 172 | } else { 173 | selectedStyleSource.removeSharedStyle(selectedStyle); 174 | } 175 | } 176 | 177 | NSApplication.sharedApplication().delegate().runPluginCommandWithIdentifier_fromBundleAtURL_context("find",context.command.pluginBundle().url(),context); 178 | } else return false; 179 | } 180 | 181 | var report = function(context) { 182 | openUrl("https://github.com/sonburn/shared-style-finder/issues/new"); 183 | 184 | if (!debugMode) googleAnalytics(context,"report","report"); 185 | } 186 | 187 | var plugins = function(context) { 188 | openUrl("https://sonburn.github.io/"); 189 | 190 | if (!debugMode) googleAnalytics(context,"plugins","plugins"); 191 | } 192 | 193 | var donate = function(context) { 194 | openUrl("https://www.paypal.me/sonburn"); 195 | 196 | if (!debugMode) googleAnalytics(context,"donate","donate"); 197 | } 198 | 199 | function createBoldLabel(text,size,frame) { 200 | var label = NSTextField.alloc().initWithFrame(frame); 201 | 202 | label.setStringValue(text); 203 | label.setFont(NSFont.boldSystemFontOfSize(size)); 204 | label.setBezeled(0); 205 | label.setDrawsBackground(0); 206 | label.setEditable(0); 207 | label.setSelectable(0); 208 | 209 | return label; 210 | } 211 | 212 | function createCheckbox(item,state,frame) { 213 | var checkbox = NSButton.alloc().initWithFrame(frame), 214 | state = (state == false) ? NSOffState : NSOnState; 215 | 216 | checkbox.setButtonType(NSSwitchButton); 217 | checkbox.setBezelStyle(0); 218 | checkbox.setTitle(item.name); 219 | checkbox.setTag(item.value); 220 | checkbox.setState(state); 221 | 222 | return checkbox; 223 | } 224 | 225 | function createContentView(frame) { 226 | var view = NSView.alloc().initWithFrame(frame); 227 | 228 | view.setFlipped(1); 229 | 230 | return view; 231 | } 232 | 233 | function createDivider(frame) { 234 | var divider = NSView.alloc().initWithFrame(frame); 235 | 236 | divider.setWantsLayer(1); 237 | divider.layer().setBackgroundColor(CGColorCreateGenericRGB(204/255,204/255,204/255,1.0)); 238 | 239 | return divider; 240 | } 241 | 242 | function createField(string,frame) { 243 | var textField = NSTextField.alloc().initWithFrame(frame); 244 | 245 | textField.setStringValue(string); 246 | textField.setFont(NSFont.systemFontOfSize(11)); 247 | textField.setTextColor(NSColor.blackColor()); 248 | textField.setBezeled(0); 249 | textField.setEditable(0); 250 | textField.setDrawsBackground(0); 251 | textField.setLineBreakMode(NSLineBreakByTruncatingTail); 252 | 253 | return textField; 254 | } 255 | 256 | function createMatchText(string,frame) { 257 | var textField = NSTextField.alloc().initWithFrame(frame), 258 | textColor = (isUsingDarkTheme()) ? NSColor.lightGrayColor() : NSColor.darkGrayColor(); 259 | 260 | textField.setStringValue(string); 261 | textField.setFont(NSFont.systemFontOfSize(11)); 262 | textField.setTextColor(textColor); 263 | textField.setBezeled(0); 264 | textField.setEditable(0); 265 | textField.setDrawsBackground(0); 266 | 267 | return textField; 268 | } 269 | 270 | function createImageArea(instance,frame) { 271 | var imageArea = NSButton.alloc().initWithFrame(frame); 272 | 273 | imageArea.setTitle(""); 274 | imageArea.setBordered(0); 275 | imageArea.setWantsLayer(1); 276 | imageArea.layer().setBackgroundColor(CGColorCreateGenericRGB(248/255,248/255,248/255,1.0)); 277 | 278 | var exportRequest = MSExportRequest.exportRequestsFromExportableLayer_inRect_useIDForName_( 279 | instance, 280 | instance.absoluteInfluenceRect(), 281 | false 282 | ).firstObject(); 283 | 284 | exportRequest.format = "png"; 285 | 286 | var scaleX = (frame.size.width-4*2) / exportRequest.rect().size.width; 287 | var scaleY = (frame.size.height-4*2) / exportRequest.rect().size.height; 288 | 289 | exportRequest.scale = (scaleX < scaleY) ? scaleX : scaleY; 290 | 291 | var colorSpace = NSColorSpace.sRGBColorSpace(), 292 | exporter = MSExporter.exporterForRequest_colorSpace_(exportRequest,colorSpace), 293 | imageRep = exporter.bitmapImageRep(), 294 | instanceImage = NSImage.alloc().init().autorelease(); 295 | 296 | instanceImage.addRepresentation(imageRep); 297 | 298 | imageArea.setImage(instanceImage); 299 | 300 | return imageArea; 301 | } 302 | 303 | function createLabel(string,frame) { 304 | var textLabel = NSTextField.alloc().initWithFrame(frame); 305 | 306 | textLabel.setStringValue(string); 307 | textLabel.setFont(NSFont.systemFontOfSize(9)); 308 | textLabel.setTextColor(NSColor.lightGrayColor()); 309 | textLabel.setBezeled(0); 310 | textLabel.setEditable(0); 311 | textLabel.setBackgroundColor(NSColor.whiteColor()); 312 | 313 | return textLabel; 314 | } 315 | 316 | function createListItem(instance,frame) { 317 | var listItem = NSView.alloc().initWithFrame(frame), 318 | rightColWidth = 140, 319 | leftColWidth = frame.size.width-rightColWidth, 320 | leftPad = 8; 321 | 322 | listItem.setFlipped(1); 323 | listItem.addSubview(createLabel("Page",NSMakeRect(leftPad,6,leftColWidth,14))); 324 | listItem.addSubview(createField(instance.parentPage().name(),NSMakeRect(leftPad,18,leftColWidth-leftPad,18))); 325 | listItem.addSubview(createLabel("Artboard",NSMakeRect(leftPad,34,leftColWidth,14))); 326 | listItem.addSubview(createField((instance.parentArtboard()) ? instance.parentArtboard().name() : "None",NSMakeRect(leftPad,46,leftColWidth-leftPad,18))); 327 | listItem.addSubview(createLabel("Instance",NSMakeRect(leftPad,62,leftColWidth,14))); 328 | listItem.addSubview(createField(instance.name(),NSMakeRect(leftPad,74,leftColWidth-leftPad,18))); 329 | listItem.addSubview(createImageArea(instance,NSMakeRect(leftColWidth,0,rightColWidth,frame.size.height))); 330 | listItem.addSubview(createDivider(NSMakeRect(0,frame.size.height-1,frame.size.width,1))); 331 | listItem.addSubview(createTargetArea(instance,NSMakeRect(0,0,frame.size.width,frame.size.height))); 332 | 333 | return listItem; 334 | } 335 | 336 | function createScrollView(frame) { 337 | var view = NSScrollView.alloc().initWithFrame(frame); 338 | 339 | view.setHasVerticalScroller(1); 340 | 341 | return view; 342 | } 343 | 344 | function createSegmentedControl(items,frame) { 345 | var segControl = NSSegmentedControl.alloc().initWithFrame(frame); 346 | 347 | segControl.setSegmentCount(items.length); 348 | 349 | items.forEach(function(item,index) { 350 | segControl.setLabel_forSegment(item,index); 351 | segControl.setWidth_forSegment(0,index); 352 | }); 353 | 354 | segControl.cell().setTrackingMode(0); //Raw value of NSSegmentSwitchTrackingSelectOne. 355 | segControl.setSelected_forSegment(1,0); 356 | 357 | return segControl; 358 | } 359 | 360 | function createSelect(items,selectedItemIndex,frame) { 361 | var comboBox = NSComboBox.alloc().initWithFrame(frame), 362 | selectedItemIndex = (selectedItemIndex > -1) ? selectedItemIndex : 0; 363 | 364 | comboBox.addItemsWithObjectValues(items); 365 | comboBox.selectItemAtIndex(selectedItemIndex); 366 | comboBox.setNumberOfVisibleItems(16); 367 | comboBox.setEditable(0); 368 | 369 | return comboBox; 370 | } 371 | 372 | function createTargetArea(instance,frame) { 373 | var targetArea = NSButton.alloc().initWithFrame(frame); 374 | 375 | uiButtons.push(targetArea); 376 | 377 | targetArea.addCursorRect_cursor(targetArea.frame(),NSCursor.pointingHandCursor()); 378 | targetArea.setTransparent(1); 379 | targetArea.setAction("callAction:"); 380 | targetArea.setCOSJSTargetFunction(function(sender) { 381 | for (var i = 0; i < uiButtons.length; i++) { 382 | if (uiButtons[i].layer()) uiButtons[i].layer().setBorderWidth(0); 383 | } 384 | 385 | sender.setWantsLayer(1); 386 | sender.layer().setBorderWidth(2); 387 | sender.layer().setBorderColor(CGColorCreateGenericRGB(0,0,1,1)); 388 | 389 | var rect = (instance.parentArtboard()) ? instance.parentArtboard().rect() : instance.absoluteRect().rect(); 390 | 391 | MSDocument.currentDocument().setCurrentPage(instance.parentPage()); 392 | MSDocument.currentDocument().contentDrawView().zoomToFitRect(rect); 393 | 394 | instance.select_byExtendingSelection(1,0); 395 | }); 396 | 397 | return targetArea; 398 | } 399 | 400 | function displayDialog(body,title) { 401 | var app = NSApplication.sharedApplication(); 402 | app.displayDialog_withTitle(body,title); 403 | } 404 | 405 | function getObjectsWithStyleID(styleID) { 406 | var sketch = require("sketch"), 407 | document = sketch.getSelectedDocument(), 408 | style = (document.getSharedLayerStyleWithID(styleID)) ? document.getSharedLayerStyleWithID(styleID) : document.getSharedTextStyleWithID(styleID), 409 | styleInstances = style.getAllInstances(); 410 | 411 | var objectsWithStyleID = NSMutableArray.array(); 412 | 413 | styleInstances.forEach(function(style){ 414 | objectsWithStyleID.addObject(style.getParentLayer().sketchObject); 415 | }); 416 | 417 | return objectsWithStyleID; 418 | } 419 | 420 | function getObjectsWithStyleIDOld(styleID) { 421 | var objectsWithStyleID = NSMutableArray.array(), 422 | pageLoop = MSDocument.currentDocument().pages().objectEnumerator(), 423 | page; 424 | 425 | while (page = pageLoop.nextObject()) { 426 | var predicate = NSPredicate.predicateWithFormat("style.sharedObjectID == %@",styleID), 427 | layers = page.children().filteredArrayUsingPredicate(predicate), 428 | layerLoop = layers.objectEnumerator(), 429 | layer; 430 | 431 | while (layer = layerLoop.nextObject()) { 432 | objectsWithStyleID.addObject(layer); 433 | } 434 | } 435 | 436 | return objectsWithStyleID; 437 | } 438 | 439 | function getSharedStyles(type) { 440 | if (sketch.version.sketch < 52) { 441 | var styles = (type == 0) ? MSDocument.currentDocument().documentData().layerStyles().objects() : MSDocument.currentDocument().documentData().layerTextStyles().objects(); 442 | } else { 443 | var styles = (type == 0) ? MSDocument.currentDocument().documentData().allLayerStyles() : MSDocument.currentDocument().documentData().allTextStyles(); 444 | } 445 | 446 | var sortByName = NSSortDescriptor.sortDescriptorWithKey_ascending("name",1); 447 | styles = styles.sortedArrayUsingDescriptors([sortByName]); 448 | 449 | return styles; 450 | } 451 | 452 | function getStyleNames(source) { 453 | var styleNames = []; 454 | 455 | for (var i = 0; i < source.length; i++) { 456 | var styleLibrary = AppController.sharedInstance().librariesController().libraryForShareableObject(source[i]), 457 | styleName = (styleLibrary) ? source[i].name() + " [" + styleLibrary.name() + "]" : source[i].name(); 458 | 459 | styleNames.push(String(styleName)); 460 | } 461 | 462 | return styleNames; 463 | } 464 | 465 | function getUnusedStyles(type) { 466 | var usedStyleIDs = [], 467 | pageLoop = MSDocument.currentDocument().pages().objectEnumerator(), 468 | page; 469 | 470 | while (page = pageLoop.nextObject()) { 471 | var predicate = (type == 0) ? NSPredicate.predicateWithFormat("className != %@ && style.sharedObjectID != nil","MSTextLayer") : NSPredicate.predicateWithFormat("className == %@ && style.sharedObjectID != nil","MSTextLayer"), 472 | layers = page.children().filteredArrayUsingPredicate(predicate), 473 | layerLoop = layers.objectEnumerator(), 474 | layer; 475 | 476 | while (layer = layerLoop.nextObject()) { 477 | var styleID = String(layer.style().sharedObjectID()); 478 | 479 | if (usedStyleIDs.indexOf(styleID) == -1) { 480 | usedStyleIDs.push(styleID); 481 | } 482 | } 483 | } 484 | 485 | var unusedStyles = NSMutableArray.array(), 486 | styles = (type == 0) ? MSDocument.currentDocument().documentData().layerStyles().objects() : MSDocument.currentDocument().documentData().layerTextStyles().objects(), 487 | styleLoop = styles.objectEnumerator(), 488 | style; 489 | 490 | while (style = styleLoop.nextObject()) { 491 | var styleID = String(style.objectID()); 492 | 493 | if (usedStyleIDs.indexOf(styleID) == -1) { 494 | unusedStyles.addObject(style); 495 | } 496 | } 497 | 498 | var sortByName = NSSortDescriptor.sortDescriptorWithKey_ascending("name",1); 499 | 500 | return unusedStyles.sortedArrayUsingDescriptors([sortByName]); 501 | } 502 | 503 | function googleAnalytics(context,category,action,label,value) { 504 | var trackingID = "UA-118958211-1", 505 | uuidKey = "google.analytics.uuid", 506 | uuid = NSUserDefaults.standardUserDefaults().objectForKey(uuidKey); 507 | 508 | if (!uuid) { 509 | uuid = NSUUID.UUID().UUIDString(); 510 | NSUserDefaults.standardUserDefaults().setObject_forKey(uuid,uuidKey); 511 | } 512 | 513 | var url = "https://www.google-analytics.com/collect?v=1"; 514 | // Tracking ID 515 | url += "&tid=" + trackingID; 516 | // Source 517 | url += "&ds=sketch" + MSApplicationMetadata.metadata().appVersion; 518 | // Client ID 519 | url += "&cid=" + uuid; 520 | // pageview, screenview, event, transaction, item, social, exception, timing 521 | url += "&t=event"; 522 | // App Name 523 | url += "&an=" + encodeURI(context.plugin.name()); 524 | // App ID 525 | url += "&aid=" + context.plugin.identifier(); 526 | // App Version 527 | url += "&av=" + context.plugin.version(); 528 | // Event category 529 | url += "&ec=" + encodeURI(category); 530 | // Event action 531 | url += "&ea=" + encodeURI(action); 532 | // Event label 533 | if (label) { 534 | url += "&el=" + encodeURI(label); 535 | } 536 | // Event value 537 | if (value) { 538 | url += "&ev=" + encodeURI(value); 539 | } 540 | 541 | var session = NSURLSession.sharedSession(), 542 | task = session.dataTaskWithURL(NSURL.URLWithString(NSString.stringWithString(url))); 543 | 544 | task.resume(); 545 | } 546 | 547 | function isUsingDarkTheme() { 548 | return (NSUserDefaults.standardUserDefaults().stringForKey("AppleInterfaceStyle") == "Dark") ? true : false; 549 | } 550 | 551 | function openUrl(url) { 552 | NSWorkspace.sharedWorkspace().openURL(NSURL.URLWithString(url)); 553 | } 554 | 555 | function removeDuplicates(value,index,self) { 556 | return self.indexOf(value) === index; 557 | } 558 | -------------------------------------------------------------------------------- /appcast.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Shared Style Finder 5 | http://sparkle-project.org/files/sparkletestcast.xml 6 | Find instances of a shared layer or text style. 7 | en 8 | 9 | Version 1.2 10 | 11 | 13 |
  • Fix for Sketch 53.
  • 14 | 15 | ]]> 16 |
    17 | 18 |
    19 |
    20 |
    21 | -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonburn/shared-style-finder/e8ab56f4af9b026f329ba471015ce8c8a2c99768/images/logo.png -------------------------------------------------------------------------------- /images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonburn/shared-style-finder/e8ab56f4af9b026f329ba471015ce8c8a2c99768/images/screenshot.png --------------------------------------------------------------------------------