├── .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 | 
2 |
3 | Find instances of a shared layer or text style.
4 |
5 | 
6 |
7 |
8 |
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
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
--------------------------------------------------------------------------------