├── .gitignore
├── README.md
├── SketchToolbarIcon.framework
├── Resources
│ ├── Assets.car
│ └── Info.plist
├── SketchToolbarIcon
└── _CodeSignature
│ └── CodeResources
├── gh-image.png
├── package-lock.json
├── package.json
└── sketch-toolbar-icon.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # npm
2 | node_modules
3 | .npm
4 | npm-debug.log
5 |
6 | # mac
7 | .DS_Store
8 |
9 | # WebStorm
10 | .idea
11 |
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sketch Toolbar Item
2 | An skpm module for adding custom toolbar items in Sketch.
3 |
4 | 
5 |
6 | ## Installation
7 | ```bash
8 | npm install --save sketch-toolbar-item
9 | ```
10 |
11 | ## Demo
12 | To see the module in action, check out the demo plugin [https://github.com/abynim/sketch-toolbar-item-demo](https://github.com/abynim/sketch-toolbar-item-demo).
13 |
14 |
15 | ## Usage
16 |
17 | ### Import the module
18 | ```js
19 | import SketchToolbar from 'sketch-toolbar-item'
20 | ```
21 |
22 | ### Define a Startup handler for your plugin in manifest.json
23 | ```json
24 | {
25 | "commands" : [
26 | {
27 | "script": "./my-command.js",
28 | "handlers": {
29 | "actions": {
30 | "Startup": "registerToolbarActions"
31 | }
32 | }
33 | }
34 | ]
35 | }
36 | ```
37 |
38 | ### Registering toolbar items
39 | All items are registered within the Startup handler
40 | ```js
41 | export function registerToolbarActions(context) {
42 | // register items here...
43 | }
44 | ```
45 |
46 | #### Register a single toolbar action
47 | Pass in the following arguments
48 | - the context
49 | - the identifier of the command this action will trigger
50 | - and a relative path to an icon image (32x32px) from your plugin's Resources folder. To include a separate image for dark mode, separate the image paths with `|`.
51 | ```js
52 | // SketchToolbar.registerToolbarAction(context, commandIdentifier, iconImagePath)
53 | SketchToolbar.registerToolbarAction(context, 'goodbye', 'goodbye-toolbar-icon.png|goodbye-toolbar-icon-dark.png')
54 | ```
55 |
56 | #### Register a group of toolbar actions
57 | First, create specifiers for each action, then register them as a group
58 | ```js
59 | // SketchToolbar.specifierForToolbarAction(context, commandIdentifier, iconImagePath)
60 | let item1 = SketchToolbar.specifierForToolbarAction(context, 'namaste', 'namaste-toolbar-icon.png|namaste-toolbar-icon-dark.png')
61 | let item2 = SketchToolbar.specifierForToolbarAction(context, 'hello', 'hello-toolbar-icon.png|hello-toolbar-icon-dark.png')
62 |
63 | // SketchToolbar.registerToolbarGroup(context, groupIdentifier, specifiers)
64 | SketchToolbar.registerToolbarGroup(context, 'salutations', [item1, item2])
65 | ```
66 |
67 | #### Register a toolbar item with a dropdown menu
68 | First, create menuItems for each sub-item, then register them as a toolbar menu
69 | ```js
70 | // SketchToolbar.menuItemForToolbarAction(context, commandIdentifier, iconImagePath)
71 | let menuItem1 = SketchToolbar.menuItemForToolbarAction(context, 'hello', 'hello-toolbar-icon.png|hello-toolbar-icon-dark.png')
72 | let menuItem2 = SketchToolbar.menuItemForToolbarAction(context, 'namaste', 'namaste-toolbar-icon.pngnamaste-toolbar-icon-dark.png')
73 | let menuItem3 = SketchToolbar.separatorMenuItem()
74 | let menuItem4 = SketchToolbar.menuItemForToolbarAction(context, 'goodbye', 'goodbye-toolbar-icon.png|goodbye-toolbar-icon-dark.png')
75 |
76 | // SketchToolbar.registerToolbarMenu(context, menuIdentifier, iconImagePath, menuItems)
77 | SketchToolbar.registerToolbarMenu(context, 'greetings', 'Greetings', 'greetings-toolbar-icon.png|greetings-toolbar-icon-dark.png', [menuItem1, menuItem2, menuItem3, menuItem4])
78 | ```
79 |
80 | ### Validate toolbar items (optional)
81 | You can validate toolbar items when the selection changes, but please use this sparingly. Doing too much in this method will make Sketch crawl.
82 |
83 | If this method is not defined, your toolbar items will always be enabled (which is acceptable in most situations).
84 |
85 | When defining the command in manifest.json, add an additional handler for `ValidateToolbarItem`.
86 |
87 | ```json
88 | {
89 | "commands" : [
90 | {
91 | "script": "./my-command.js",
92 | "handlers": {
93 | "run": "sayHello",
94 | "ValidateToolbarItem": "validateToolbarItem"
95 | },
96 | "name": "Hello",
97 | "identifier": "hello"
98 | }
99 | ]
100 | }
101 | ```
102 |
103 | In the handler, access the toolbar item via the context, and set its enabled property.
104 | ```js
105 | export function validateToolbarItem(context) {
106 |
107 | let toolbarItem = context.toolbarItem
108 |
109 | // As an example: enable the toolbar item if selection is not empty
110 | toolbarItem.enabled = !sketch.getSelectedDocument().selectedLayers.isEmpty
111 |
112 | }
113 | ```
114 |
115 | ### Note:
116 | Please set a unique identifier for your plugin in your manifest. It's required to identify your plugin and avoid conflicts with other plugins. If using `skpm` this is already handled for you.
117 |
118 | ---
119 |
120 | Do create an issue here if you find any weirdness.
121 |
122 | Follow along on [Twitter](https://twitter.com/abynim) for more experiments and content related to Sketch plugins. I spend most of my time building a little plugin called [Sketch Runner](https://sketchrunner.com), do check it out.
123 |
--------------------------------------------------------------------------------
/SketchToolbarIcon.framework/Resources/Assets.car:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abynim/sketch-toolbar-item/511e386aaf819d72d7d85d1d4eecdb828f78f06e/SketchToolbarIcon.framework/Resources/Assets.car
--------------------------------------------------------------------------------
/SketchToolbarIcon.framework/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildMachineOSBuild
6 | 19H2
7 | CFBundleDevelopmentRegion
8 | en
9 | CFBundleExecutable
10 | SketchToolbarIcon
11 | CFBundleIdentifier
12 | com.abynim.SketchToolbarIcon
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | SketchToolbarIcon
17 | CFBundlePackageType
18 | FMWK
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSupportedPlatforms
22 |
23 | MacOSX
24 |
25 | CFBundleVersion
26 | 119
27 | DTCompiler
28 | com.apple.compilers.llvm.clang.1_0
29 | DTPlatformBuild
30 | 12A7300
31 | DTPlatformName
32 | macosx
33 | DTPlatformVersion
34 | 10.15.6
35 | DTSDKBuild
36 | 19G68
37 | DTSDKName
38 | macosx10.15
39 | DTXcode
40 | 1201
41 | DTXcodeBuild
42 | 12A7300
43 | LSMinimumSystemVersion
44 | 10.14
45 | NSHumanReadableCopyright
46 | Copyright © 2019 Aby Nimbalkar. All rights reserved.
47 |
48 |
49 |
--------------------------------------------------------------------------------
/SketchToolbarIcon.framework/SketchToolbarIcon:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abynim/sketch-toolbar-item/511e386aaf819d72d7d85d1d4eecdb828f78f06e/SketchToolbarIcon.framework/SketchToolbarIcon
--------------------------------------------------------------------------------
/SketchToolbarIcon.framework/_CodeSignature/CodeResources:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | files
6 |
7 | Resources/Assets.car
8 |
9 | stFi/ljN8Ipl9NwM6WQHnli9Ir4=
10 |
11 | Resources/Info.plist
12 |
13 | Z7U3vd+DcEwsgBWIu0Rijjj1nQo=
14 |
15 |
16 | files2
17 |
18 | Resources/Assets.car
19 |
20 | hash2
21 |
22 | Uldi7Lns+DkHx50WHk8ghcQQFlRjvecvPuRf7QJgxAM=
23 |
24 |
25 | Resources/Info.plist
26 |
27 | hash2
28 |
29 | 2n0Ty5vOsLxwFq0DPYCEABEuoHmOFOetn1JN/wRNAqM=
30 |
31 |
32 |
33 | rules
34 |
35 | ^Resources/
36 |
37 | ^Resources/.*\.lproj/
38 |
39 | optional
40 |
41 | weight
42 | 1000
43 |
44 | ^Resources/.*\.lproj/locversion.plist$
45 |
46 | omit
47 |
48 | weight
49 | 1100
50 |
51 | ^Resources/Base\.lproj/
52 |
53 | weight
54 | 1010
55 |
56 | ^version.plist$
57 |
58 |
59 | rules2
60 |
61 | .*\.dSYM($|/)
62 |
63 | weight
64 | 11
65 |
66 | ^(.*/)?\.DS_Store$
67 |
68 | omit
69 |
70 | weight
71 | 2000
72 |
73 | ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/
74 |
75 | nested
76 |
77 | weight
78 | 10
79 |
80 | ^.*
81 |
82 | ^Info\.plist$
83 |
84 | omit
85 |
86 | weight
87 | 20
88 |
89 | ^PkgInfo$
90 |
91 | omit
92 |
93 | weight
94 | 20
95 |
96 | ^Resources/
97 |
98 | weight
99 | 20
100 |
101 | ^Resources/.*\.lproj/
102 |
103 | optional
104 |
105 | weight
106 | 1000
107 |
108 | ^Resources/.*\.lproj/locversion.plist$
109 |
110 | omit
111 |
112 | weight
113 | 1100
114 |
115 | ^Resources/Base\.lproj/
116 |
117 | weight
118 | 1010
119 |
120 | ^[^/]+$
121 |
122 | nested
123 |
124 | weight
125 | 10
126 |
127 | ^embedded\.provisionprofile$
128 |
129 | weight
130 | 20
131 |
132 | ^version\.plist$
133 |
134 | weight
135 | 20
136 |
137 |
138 |
139 |
140 |
--------------------------------------------------------------------------------
/gh-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abynim/sketch-toolbar-item/511e386aaf819d72d7d85d1d4eecdb828f78f06e/gh-image.png
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sketch-toolbar-item",
3 | "version": "0.1.4",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@skpm/nib-loader": {
8 | "version": "0.1.2",
9 | "resolved": "https://registry.npmjs.org/@skpm/nib-loader/-/nib-loader-0.1.2.tgz",
10 | "integrity": "sha512-qWj31AmzEO9+BlozMaZo7HOK6v2VAkOmj9wSMaPwM54tmHDKKE5p1F+MbcAsibsfbbTfodK5PKkpimf1GyjHiA==",
11 | "dev": true,
12 | "requires": {
13 | "cocoascript-class": "^0.1.2",
14 | "loader-utils": "^1.2.3",
15 | "schema-utils": "^0.4.5"
16 | }
17 | },
18 | "@skpm/xcodeproj-loader": {
19 | "version": "0.1.6",
20 | "resolved": "https://registry.npmjs.org/@skpm/xcodeproj-loader/-/xcodeproj-loader-0.1.6.tgz",
21 | "integrity": "sha512-EuGG1cm1gjMLRLImgeJ40KyWJ1P69Qz9oNp+fKn1GaHWz3vWV3568PidtzMx3H3UM3i5bXUroMmIfj7DQaI/Dg==",
22 | "dev": true,
23 | "requires": {
24 | "@skpm/nib-loader": "^0.1.2",
25 | "loader-utils": "^1.2.3",
26 | "schema-utils": "^0.4.5"
27 | }
28 | },
29 | "ajv": {
30 | "version": "6.10.1",
31 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.1.tgz",
32 | "integrity": "sha512-w1YQaVGNC6t2UCPjEawK/vo/dG8OOrVtUmhBT1uJJYxbl5kU2Tj3v6LGqBcsysN1yhuCStJCCA3GqdvKY8sqXQ==",
33 | "dev": true,
34 | "requires": {
35 | "fast-deep-equal": "^2.0.1",
36 | "fast-json-stable-stringify": "^2.0.0",
37 | "json-schema-traverse": "^0.4.1",
38 | "uri-js": "^4.2.2"
39 | }
40 | },
41 | "ajv-keywords": {
42 | "version": "3.4.1",
43 | "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz",
44 | "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==",
45 | "dev": true
46 | },
47 | "big.js": {
48 | "version": "5.2.2",
49 | "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
50 | "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
51 | "dev": true
52 | },
53 | "cocoascript-class": {
54 | "version": "0.1.2",
55 | "resolved": "https://registry.npmjs.org/cocoascript-class/-/cocoascript-class-0.1.2.tgz",
56 | "integrity": "sha1-2rJfIDiZRtmYbBgSuIrDeD7sQtM=",
57 | "dev": true
58 | },
59 | "emojis-list": {
60 | "version": "2.1.0",
61 | "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
62 | "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
63 | "dev": true
64 | },
65 | "fast-deep-equal": {
66 | "version": "2.0.1",
67 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
68 | "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
69 | "dev": true
70 | },
71 | "fast-json-stable-stringify": {
72 | "version": "2.0.0",
73 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
74 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
75 | "dev": true
76 | },
77 | "json-schema-traverse": {
78 | "version": "0.4.1",
79 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
80 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
81 | "dev": true
82 | },
83 | "json5": {
84 | "version": "1.0.1",
85 | "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
86 | "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
87 | "dev": true,
88 | "requires": {
89 | "minimist": "^1.2.0"
90 | }
91 | },
92 | "loader-utils": {
93 | "version": "1.2.3",
94 | "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
95 | "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
96 | "dev": true,
97 | "requires": {
98 | "big.js": "^5.2.2",
99 | "emojis-list": "^2.0.0",
100 | "json5": "^1.0.1"
101 | }
102 | },
103 | "minimist": {
104 | "version": "1.2.5",
105 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
106 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
107 | "dev": true
108 | },
109 | "punycode": {
110 | "version": "2.1.1",
111 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
112 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
113 | "dev": true
114 | },
115 | "schema-utils": {
116 | "version": "0.4.7",
117 | "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz",
118 | "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==",
119 | "dev": true,
120 | "requires": {
121 | "ajv": "^6.1.0",
122 | "ajv-keywords": "^3.1.0"
123 | }
124 | },
125 | "uri-js": {
126 | "version": "4.2.2",
127 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
128 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
129 | "dev": true,
130 | "requires": {
131 | "punycode": "^2.1.0"
132 | }
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sketch-toolbar-item",
3 | "version": "0.1.5",
4 | "description": "A module for adding toolbar items in Sketch",
5 | "main": "sketch-toolbar-icon.js",
6 | "scripts": {
7 | "test": "echo \"No test specified\""
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/abynim/sketch-toolbar-item.git"
12 | },
13 | "author": "Aby Nimbalkar",
14 | "license": "MIT",
15 | "bugs": {
16 | "url": "https://github.com/abynim/sketch-toolbar-item/issues"
17 | },
18 | "homepage": "https://github.com/abynim/sketch-toolbar-item#readme",
19 | "devDependencies": {
20 | "@skpm/xcodeproj-loader": "^0.1.6"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/sketch-toolbar-icon.js:
--------------------------------------------------------------------------------
1 | const SketchToolbarIconClass = require('@skpm/xcodeproj-loader?raw=true&publicPath=_webpack_resources&outputPath=../Resources/_webpack_resources!./SketchToolbarIcon.framework/SketchToolbarIcon').getClass('SketchToolbarIcon');
2 |
3 | module.exports = function() {
4 |
5 | let o = {};
6 |
7 | /**
8 | * @param {any} context - The current context
9 | * @param {string} commandID - The identifier of the command this item will trigger
10 | * @param {string} iconImagePath - A relative path to a 32x32px png image. To include a separate image path for dark mode use | to separate their path names
11 | */
12 | o.registerToolbarAction = function(context, commandID, iconImagePath) {
13 | SketchToolbarIconClass.registerToolbarAction_commandID_iconImagePath(context, commandID, iconImagePath);
14 | }
15 |
16 | /**
17 | * @param {any} context - The current context
18 | * @param {string} commandID - The identifier of the command this item will trigger
19 | * @param {string} iconImagePath - A relative path to a 32x32px png image. To include a separate image path for dark mode use | to separate their path names
20 | * @returns {any} A toolbar item specifier which can be used to register a toolbar item group
21 | */
22 | o.specifierForToolbarAction = function(context, commandID, iconImagePath) {
23 | return SketchToolbarIconClass.specifierForToolbarAction_commandID_iconImagePath(context, commandID, iconImagePath);
24 | }
25 |
26 | /**
27 | * @param {any} context - The current context
28 | * @param {string} identifier - A unique identifier for the group item
29 | * @param {Array} specifiers - An array of specifiers created using `specifierForToolbarAction`
30 | */
31 | o.registerToolbarGroup = function (context, identifier, specifiers) {
32 | SketchToolbarIconClass.registerToolbarGroup_identifier_specifiers(context, identifier, specifiers);
33 | }
34 |
35 | /**
36 | * @param {any} context - The current context
37 | * @param {string} commandID - A unique identifier for the group item
38 | * @param {string} iconImagePath - A relative path to a 32x32px png image. To include a separate image path for dark mode use | to separate their path names
39 | * @returns {any} A menu item specifier to be used when registering a toolbar item with a dropdown menu
40 | */
41 | o.menuItemForToolbarAction = function(context, commandID, iconImagePath) {
42 | return SketchToolbarIconClass.menuItemForToolbarAction_commandID_iconImagePath(context, commandID, iconImagePath);
43 | }
44 |
45 | /**
46 | * @returns {any} A separator menu item specifier to be used when registering a toolbar item with a dropdown menu
47 | */
48 | o.separatorMenuItem = function () {
49 | return SketchToolbarIconClass.separatorMenuItem();
50 | }
51 |
52 | /**
53 | * @param {any} context - The current context
54 | * @param {string} identifier - A unique identifier for the toolbar item
55 | * @param {string} title - The text to be displayed below in the toolbar item
56 | * @param {string} iconImagePath - A relative path to a 32x32px png image. To include a separate image path for dark mode use | to separate their path names
57 | * @param {Array} menuItems - An array of menu item specifiers created using `menuItemForToolbarAction` or `separatorMenuItem`
58 | */
59 | o.registerToolbarMenu = function (context, identifier, title, iconImagePath, menuItems) {
60 | SketchToolbarIconClass.registerToolbarMenu_identifier_title_iconImagePath_menuItems(context, identifier, title, iconImagePath, menuItems);
61 | }
62 |
63 | return o;
64 |
65 | }();
--------------------------------------------------------------------------------