├── .appcast.xml ├── .gitignore ├── README.md ├── assets └── icon.png ├── package-lock.json ├── package.json ├── sketch-assets └── icons.sketch ├── src ├── manifest.json └── switch_theme.js └── switch-light-dark.sketchplugin └── Contents ├── Resources └── icon.png └── Sketch ├── manifest.json └── switch_theme.js /.appcast.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build artifacts 2 | plugin.sketchplugin 3 | 4 | # npm 5 | node_modules 6 | .npm 7 | npm-debug.log 8 | 9 | # mac 10 | .DS_Store 11 | 12 | # WebStorm 13 | .idea 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Automatically switch designs between Dark-Light modes 2 | 3 | This plugin switches one or multiple artboards between light/dark modes instantly. It can work with symbols, nested symbols, overrides, shapes, layer styles, text styles and colour variables. 4 | 5 | 6 | 7 | ## Usage 8 | Have one or more libraries linked in Sketch for this to work. 9 | 10 | It can switch: 11 | - symbols 12 | - nested symbols 13 | - all override types 14 | - texts 15 | - layer styles 16 | - text styles 17 | - NEW: colour variables 18 | 19 | All of these must have 'dark' or 'light' somewhere in their name, doesn't matter where. 20 | Just double click on the .sketchplugin file in the archive downloaded and you're good to go! 21 | 22 | ## Debugging 23 | 24 | To view the output of your `console.log`, you have a few different options: 25 | 26 | - Use the [`sketch-dev-tools`](https://github.com/skpm/sketch-dev-tools) 27 | - Open `Console.app` and look for the sketch logs 28 | - Look at the `~/Library/Logs/com.bohemiancoding.sketch3/Plugin Output.log` file 29 | 30 | Skpm provides a convenient way to do the latter: 31 | 32 | ```bash 33 | skpm log 34 | ``` 35 | 36 | The `-f` option causes `skpm log` to not stop when the end of logs is reached, but rather to wait for additional data to be appended to the input 37 | -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaos-machine/switch-theme/180588fede36f0940e2cb67f3d5a0772f9c24296/assets/icon.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "switch_theme", 3 | "description": "Switch colour variables, symbols, overrides, layer styles and text styles", 4 | "author": "Tudor Teisanu ", 5 | "version": "2.3.0", 6 | "engines": { 7 | "sketch": ">=3.0" 8 | }, 9 | "skpm": { 10 | "name": "Light/Dark Switch", 11 | "manifest": "src/manifest.json", 12 | "main": "switch-light-dark.sketchplugin", 13 | "assets": [ 14 | "assets/**/*" 15 | ], 16 | "sketch-assets-file": "sketch-assets/icons.sketch" 17 | }, 18 | "scripts": { 19 | "build": "skpm-build", 20 | "watch": "skpm-build --watch", 21 | "start": "skpm-build --watch --run", 22 | "postinstall": "npm run build && skpm-link" 23 | }, 24 | "devDependencies": { 25 | "@skpm/builder": "^0.4.0", 26 | "@skpm/nib-loader": "^0.1.2", 27 | "appkit": "^1.0.1" 28 | }, 29 | "dependencies": { 30 | "common-js": "^0.3.8", 31 | "sketch-toolbar-item": "^0.1.4" 32 | }, 33 | "repository": { 34 | "type": "git", 35 | "url": "https://github.com/chaos-machine/switch-theme.git" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sketch-assets/icons.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaos-machine/switch-theme/180588fede36f0940e2cb67f3d5a0772f9c24296/sketch-assets/icons.sketch -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/sketch-hq/SketchAPI/develop/docs/sketch-plugin-manifest-schema.json", 3 | "icon": "icon.png", 4 | "homepage": "https://github.com/chaos-machine/switch-theme", 5 | "version": "2.3.0", 6 | "identifier": "com.sketchapp.examples.switchtheme", 7 | "bundleVersion": 1, 8 | "commands": [ 9 | { 10 | "shortcut": "ctrl shift t", 11 | "script": "switch_theme.js", 12 | "name": "Switch Theme", 13 | "identifier": "switchtheme" 14 | 15 | 16 | } 17 | ], 18 | "menu": { 19 | "isRoot": true, 20 | "items": [ 21 | "switchtheme" 22 | ] 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/switch_theme.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | let sketch = require('sketch') 4 | let Artboard = sketch.Artboard 5 | var Text = require('sketch').Text 6 | var ShapePath = require('sketch/dom').ShapePath 7 | var Group = require('sketch/dom').Group 8 | 9 | 10 | let document = sketch.getSelectedDocument() 11 | let page = document.selectedPage 12 | let docSwatches = document.swatches 13 | let imported_swatches = [] 14 | 15 | var selection = context.selection; 16 | var fromTheme, toTheme; 17 | var currentArtboard 18 | var newArtboardName 19 | var instance 20 | var group_name 21 | 22 | var move = 0; 23 | var master_iterator = -1; 24 | 25 | var CreateArtboard = true; 26 | let TextLibrary 27 | let ColourLibrary 28 | 29 | var isGlobalColour = false; 30 | var isGlobalText = false; 31 | 32 | var myArtboard 33 | 34 | var layerstyleReferences 35 | var textstyleReferences 36 | let swatchRefs 37 | 38 | 39 | function removeArtboardNames() { 40 | 41 | //removes current artboard naming 42 | 43 | if (newArtboardName.includes("Dark")) { newArtboardName = newArtboardName.replace('Dark', '') } 44 | else if (newArtboardName.includes("Light")) { newArtboardName = newArtboardName.replace('Light', ''); } 45 | 46 | if (newArtboardName.includes("dark")) { newArtboardName = newArtboardName.replace('dark', '') } 47 | else if (newArtboardName.includes("light")) { newArtboardName = newArtboardName.replace('light', ''); } 48 | 49 | if (newArtboardName.includes("dakr")) { newArtboardName = newArtboardName.replace('dakr', '') } 50 | else if (newArtboardName.includes("lihgt")) { newArtboardName = newArtboardName.replace('lihgt', ''); } 51 | } 52 | 53 | function createArtboatd() { 54 | if (CreateArtboard) { 55 | myArtboard = new Artboard({ 56 | parent: page, 57 | name: newArtboardName + ' ' + toTheme, 58 | frame: { 59 | x: currentArtboard.frame.x + currentArtboard.frame.width + move, 60 | y: currentArtboard.frame.y, 61 | width: currentArtboard.frame.width, 62 | height: currentArtboard.frame.height 63 | } 64 | }); 65 | 66 | myArtboard.background.enabled = true; 67 | 68 | if (toTheme == "Dark") { 69 | myArtboard.background.color = '#000000ff'; 70 | } 71 | CreateArtboard = false 72 | } 73 | 74 | } 75 | 76 | 77 | var libraries = require('sketch/dom').getLibraries() 78 | for (var l = 0; l < Object.keys(libraries).length; l++) { 79 | if (libraries[l].name.includes('Type') || libraries[l].name.includes('Text') || libraries[l].name.includes('Fonts') || libraries[l].name.includes('Fundamentals')) { 80 | isGlobalText = true; 81 | TextLibrary = sketch.getLibraries()[l] 82 | textstyleReferences = TextLibrary.getImportableTextStyleReferencesForDocument(document) 83 | 84 | } 85 | 86 | if (libraries[l].name.includes('Color') || libraries[l].name.includes('Fundamentals') || libraries[l].name.includes('Colours')) { 87 | isGlobalColour = true; 88 | ColourLibrary = sketch.getLibraries()[l] 89 | layerstyleReferences = ColourLibrary.getImportableLayerStyleReferencesForDocument(document) 90 | swatchRefs = ColourLibrary.getImportableSwatchReferencesForDocument(document) 91 | 92 | } 93 | } 94 | 95 | 96 | 97 | //for each layer selected in the document, checking if there are artboards with sublayers 98 | 99 | 100 | 101 | var UI = require('sketch/ui') 102 | 103 | 104 | UI.getInputFromUser( 105 | "Switch to", 106 | { 107 | type: UI.INPUT_TYPE.selection, 108 | possibleValues: ['Light', 'Dark'], 109 | }, 110 | (err, value) => { 111 | if (err) { 112 | UI.close(); 113 | return 114 | } 115 | 116 | if (value == 'Light') { 117 | fromTheme = 'Dark' 118 | toTheme = 'Light' 119 | 120 | } 121 | if (value == 'Dark') { 122 | fromTheme = 'Light' 123 | toTheme = 'Dark' 124 | 125 | } 126 | } 127 | ) 128 | 129 | 130 | 131 | selection.forEach(function (layer) { 132 | if (layer.layers && layer.class() == "MSArtboardGroup") { 133 | master_iterator++; move += 150; 134 | var children = layer.children(); 135 | currentArtboard = sketch.getSelectedDocument().selectedLayers.layers[master_iterator] 136 | 137 | //artboard name handling 138 | 139 | newArtboardName = sketch.getSelectedDocument().selectedLayers.layers[master_iterator].name; 140 | removeArtboardNames(); 141 | 142 | CreateArtboard = true; 143 | createArtboatd(); 144 | 145 | 146 | //////////GETTING ALL LAYERS IN AN ARTBOARD BY NAME, THEN ID 147 | 148 | for (var f = 0; f < children.count(); f++) { 149 | if (children[f].class() != "MSArtboardGroup") { 150 | var extracted_children_id = children[f].toString().split('(').pop().split(')')[0]; 151 | var current_layer_in_artboard = document.getLayerWithID(extracted_children_id) 152 | 153 | //////////SYMBOL LAYER 154 | 155 | if (current_layer_in_artboard.type == "SymbolInstance") { 156 | 157 | //I use the extracted ID and found layer to match the correct library & master references 158 | 159 | var library_linked = current_layer_in_artboard.master.getLibrary() 160 | var symbolReferences = library_linked.getImportableSymbolReferencesForDocument(document) 161 | let swatchRefs = library_linked.getImportableSwatchReferencesForDocument(document) 162 | 163 | if (!isGlobalColour){ 164 | layerstyleReferences = library_linked.getImportableLayerStyleReferencesForDocument(document) 165 | } 166 | if (!isGlobalText) { 167 | textstyleReferences = library_linked.getImportableTextStyleReferencesForDocument(document) 168 | } 169 | 170 | 171 | 172 | symbolReferences.forEach((ImportableObject) => { 173 | if (ImportableObject.id == current_layer_in_artboard.symbolId) { 174 | var imported_symbol_name = ImportableObject.name.replace(fromTheme, toTheme) 175 | var to_Import = symbolReferences.filter(element => element.name == imported_symbol_name) 176 | var symbolMaster = to_Import[0].import(); 177 | instance = symbolMaster.createNewInstance(); 178 | 179 | //sizes checked to match symbol to existing instance 180 | 181 | var new_frame = current_layer_in_artboard.frame 182 | var new_x = new_frame.x 183 | var new_y = new_frame.y 184 | var new_width = new_frame.width 185 | var new_height = new_frame.height 186 | var new_rotation = current_layer_in_artboard.transform.rotation 187 | var new_transform = current_layer_in_artboard.transform 188 | var new_instance_name = current_layer_in_artboard.name 189 | 190 | instance.name = new_instance_name 191 | instance.transform = new_transform 192 | instance.transform.rotation = new_rotation; 193 | instance.frame = { x: new_x, y: new_y, width: new_width, height: new_height }; 194 | instance.parent = myArtboard; 195 | 196 | 197 | //going through overrides for symbols and texts separately 198 | 199 | for (var w = 0; w < (Object.keys(instance.overrides).length); w++) { 200 | 201 | //only get the override if the value is differnet from the default one, otherwise let the symbol handle it 202 | 203 | //looking at overriding text 204 | if (current_layer_in_artboard.overrides[w].property == 'stringValue' && !(current_layer_in_artboard.overrides[w].isDefault)) { 205 | instance.overrides[w].value = current_layer_in_artboard.overrides[w].value 206 | } 207 | 208 | if (current_layer_in_artboard.overrides[w].property == 'layerStyle' && !(current_layer_in_artboard.overrides[w].isDefault)) { 209 | layerstyleReferences.forEach((element) => { 210 | if (element.id == current_layer_in_artboard.overrides[w].value) { 211 | var layer_style_switch_name = element.name.replace(fromTheme, toTheme) 212 | layerstyleReferences.forEach((sub_element) => { 213 | 214 | if (sub_element.name == layer_style_switch_name) { 215 | 216 | var sharedStyle = sub_element.import() 217 | instance.overrides[w].value = sharedStyle.id 218 | } 219 | 220 | }) 221 | } 222 | }) 223 | } 224 | 225 | if (current_layer_in_artboard.overrides[w].property == 'textStyle' && !(current_layer_in_artboard.overrides[w].isDefault)) { 226 | 227 | textstyleReferences.forEach((element) => { 228 | 229 | if (element.id == current_layer_in_artboard.overrides[w].value) { 230 | var text_stlye_override_switch_name = element.name.replace(fromTheme, toTheme) 231 | //look for the switched text style by its name 232 | 233 | textstyleReferences.forEach((sub_element) => { 234 | if (sub_element.name == text_stlye_override_switch_name) { 235 | var textStyle_to_import = sub_element.import() 236 | instance.overrides[w].value = textStyle_to_import.id 237 | } 238 | }); 239 | 240 | } 241 | }) 242 | } 243 | 244 | if (current_layer_in_artboard.overrides[w].property == 'symbolID' && !(current_layer_in_artboard.overrides[w].isDefault)) { 245 | var override_symbol_master = document.getSymbolMasterWithID(current_layer_in_artboard.overrides[w].value) 246 | var override_library_linked = override_symbol_master.getLibrary() 247 | 248 | var override_symbolReferences = override_library_linked.getImportableSymbolReferencesForDocument(document) 249 | 250 | override_symbolReferences.forEach((element) => { 251 | 252 | if (element.id == current_layer_in_artboard.overrides[w].value) { 253 | 254 | 255 | var symbol_override_switch_name = element.name.replace(fromTheme, toTheme) 256 | //look for the switched symbol by its name 257 | 258 | override_symbolReferences.forEach((sub_element) => { 259 | if (sub_element.name == symbol_override_switch_name) { 260 | 261 | //importing the same symbol into the artboard , deleting it afterward to have it as a reference 262 | 263 | var master_to_delte = sub_element.import(); 264 | var instance_to_delete = master_to_delte.createNewInstance(); 265 | 266 | instance_to_delete.parent = myArtboard 267 | instance.overrides[w].value = instance_to_delete.symbolId 268 | instance_to_delete.remove() 269 | 270 | } 271 | 272 | }); 273 | } 274 | }) 275 | } 276 | 277 | } 278 | } 279 | 280 | }) 281 | } 282 | 283 | //////////TEXT LAYER 284 | 285 | if (current_layer_in_artboard.type == 'Text') { 286 | var text = new Text({ 287 | text: current_layer_in_artboard.text, 288 | frame: current_layer_in_artboard.frame, 289 | style: current_layer_in_artboard.style, 290 | name: current_layer_in_artboard.name, 291 | sharedStyleId: current_layer_in_artboard.sharedStyleId, 292 | sharedStyle: current_layer_in_artboard.sharedStyle 293 | }) 294 | text.behaviour = current_layer_in_artboard.behaviour; 295 | text.parent = myArtboard 296 | 297 | if (text.sharedStyle != null) { 298 | if (text.sharedStyle.name.includes(fromTheme)) { 299 | var switch_text_style = text.sharedStyle.name.replace(fromTheme, toTheme) 300 | 301 | textstyleReferences.forEach((element) => { 302 | if (element.name == switch_text_style) { 303 | var new_text_style = element.import(); 304 | text.sharedStyle = new_text_style; 305 | text.style.syncWithSharedStyle(new_text_style) 306 | } 307 | }) 308 | 309 | } 310 | 311 | } 312 | else { 313 | //there are no text styles, just local swatches used 314 | 315 | if (!isGlobalColour) { 316 | log("Looking into local swatches for text colours") 317 | log("Current text color" + text.style.textColor) 318 | docSwatches.forEach((element) => { 319 | if (element.color == text.style.textColor) { 320 | var swatch_switch = element.name 321 | swatch_switch = swatch_switch.replace(fromTheme, toTheme) 322 | docSwatches.forEach((sub_swatch) => { 323 | if (sub_swatch.name == swatch_switch) { 324 | text.style.textColor = sub_swatch.referencingColor 325 | } 326 | }) 327 | 328 | } 329 | }) 330 | } 331 | 332 | //No text styles, global swatches are used 333 | else { 334 | var global_swatch_switch_text 335 | log("Looking into global swatches") 336 | 337 | //importing all swatches into an array to get colours and names 338 | 339 | swatchRefs.forEach((element) => { 340 | imported_swatches.push(element.import()) 341 | }) 342 | /*finding which swatch is used currently based on color code */ 343 | imported_swatches.forEach((sub_element) => { 344 | if (sub_element.color == text.style.textColor) { 345 | log("Current swatch is" + sub_element.name) 346 | 347 | global_swatch_switch_text = sub_element.name 348 | global_swatch_switch_text = global_swatch_switch_text.replace(fromTheme, toTheme) 349 | } 350 | }) 351 | /*finding out the alternative darl/light swatch, then replacing it for the shape */ 352 | imported_swatches.forEach((sub_sub_element) => { 353 | if (sub_sub_element.name == global_swatch_switch_text) { 354 | text.style.textColor = sub_sub_element.referencingColor 355 | } 356 | }) 357 | 358 | } 359 | 360 | 361 | } 362 | } 363 | 364 | 365 | //////////SHAPE LAYER 366 | 367 | if (current_layer_in_artboard.type == 'ShapePath') { 368 | const shapePath = new ShapePath({ 369 | shapeType: current_layer_in_artboard.shapeType, 370 | sharedStyle: current_layer_in_artboard.sharedStyle, 371 | frame: current_layer_in_artboard.frame, 372 | points: current_layer_in_artboard.points, 373 | transform: current_layer_in_artboard.transform, 374 | name: current_layer_in_artboard.name, 375 | style: current_layer_in_artboard.style, 376 | parent: myArtboard 377 | 378 | }) 379 | shapePath.transform.rotation = current_layer_in_artboard.transform.rotation 380 | 381 | if (shapePath.sharedStyle != null) { 382 | //layerstyleReferences only looks at layerstyles imported from the previous symbol in the document. 383 | layerstyleReferences.forEach((element) => { 384 | if (element.id == shapePath.sharedStyleId) { 385 | 386 | var switch_layer_style = element.name 387 | switch_layer_style = switch_layer_style.replace(fromTheme, toTheme) 388 | 389 | layerstyleReferences.forEach((sub_element) => { 390 | if (sub_element.name == switch_layer_style) { 391 | var new_new_layer_style = sub_element.import(); 392 | shapePath.sharedStyleId = new_new_layer_style.id; 393 | shapePath.style.syncWithSharedStyle(new_new_layer_style) 394 | } 395 | }) 396 | } 397 | }) 398 | } 399 | 400 | else { 401 | //shapes using colour variables/swatches. Check if swatches are in a Colour global library or not 402 | 403 | if (!isGlobalColour) { 404 | log("Looking into local swatches") 405 | docSwatches.forEach((element) => { 406 | if (element.color == shapePath.style.fills[0].color) { 407 | var swatch_switch = element.name 408 | swatch_switch = swatch_switch.replace(fromTheme, toTheme) 409 | docSwatches.forEach((sub_swatch) => { 410 | if (sub_swatch.name == swatch_switch) { 411 | shapePath.style.fills[0].color = sub_swatch.referencingColor 412 | } 413 | }) 414 | 415 | } 416 | }) 417 | } 418 | else { 419 | var global_swatch_switch 420 | log("Looking into global swatches") 421 | 422 | //importing all swatches into an array to get colours and names 423 | 424 | swatchRefs.forEach((element) => { 425 | imported_swatches.push(element.import()) 426 | }) 427 | /*finding which swatch is used currently based on color code */ 428 | imported_swatches.forEach((sub_element) => { 429 | if (sub_element.color == shapePath.style.fills[0].color) { 430 | log("Current swatch is" + sub_element.name) 431 | 432 | global_swatch_switch = sub_element.name 433 | global_swatch_switch = global_swatch_switch.replace(fromTheme, toTheme) 434 | } 435 | }) 436 | /*finding out the alternative darl/light swatch, then replacing it for the shape */ 437 | imported_swatches.forEach((sub_sub_element) => { 438 | if (sub_sub_element.name == global_swatch_switch) { 439 | shapePath.style.fills[0].color = sub_sub_element.referencingColor 440 | } 441 | }) 442 | 443 | 444 | 445 | 446 | 447 | } 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | } 456 | } 457 | } 458 | 459 | 460 | 461 | 462 | } 463 | } 464 | 465 | else 466 | { 467 | UI.message('Please select one or more artboards') 468 | } 469 | }); 470 | 471 | 472 | 473 | 474 | -------------------------------------------------------------------------------- /switch-light-dark.sketchplugin/Contents/Resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaos-machine/switch-theme/180588fede36f0940e2cb67f3d5a0772f9c24296/switch-light-dark.sketchplugin/Contents/Resources/icon.png -------------------------------------------------------------------------------- /switch-light-dark.sketchplugin/Contents/Sketch/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/sketch-hq/SketchAPI/develop/docs/sketch-plugin-manifest-schema.json", 3 | "icon": "icon.png", 4 | "homepage": "https://github.com/chaos-machine/switch-theme", 5 | "version": "2.1.0", 6 | "identifier": "com.sketchapp.examples.switchtheme", 7 | "bundleVersion": 1, 8 | "commands": [ 9 | { 10 | "shortcut": "ctrl shift t", 11 | "script": "switch_theme.js", 12 | "name": "Switch Theme", 13 | "identifier": "switchtheme" 14 | } 15 | ], 16 | "menu": { 17 | "isRoot": true, 18 | "items": [ 19 | "switchtheme" 20 | ] 21 | }, 22 | "description": "Switch colour variables, symbols, overrides, layer styles and text styles", 23 | "name": "Light/Dark Switch", 24 | "disableCocoaScriptPreprocessor": true, 25 | "appcast": "https://raw.githubusercontent.com/chaos-machine/switch-theme/master/.appcast.xml", 26 | "author": "Tudor Teisanu", 27 | "authorEmail": "tudor703344@gmail.com" 28 | } -------------------------------------------------------------------------------- /switch-light-dark.sketchplugin/Contents/Sketch/switch_theme.js: -------------------------------------------------------------------------------- 1 | var that = this; 2 | function __skpm_run (key, context) { 3 | that.context = context; 4 | 5 | var exports = 6 | /******/ (function(modules) { // webpackBootstrap 7 | /******/ // The module cache 8 | /******/ var installedModules = {}; 9 | /******/ 10 | /******/ // The require function 11 | /******/ function __webpack_require__(moduleId) { 12 | /******/ 13 | /******/ // Check if module is in cache 14 | /******/ if(installedModules[moduleId]) { 15 | /******/ return installedModules[moduleId].exports; 16 | /******/ } 17 | /******/ // Create a new module (and put it into the cache) 18 | /******/ var module = installedModules[moduleId] = { 19 | /******/ i: moduleId, 20 | /******/ l: false, 21 | /******/ exports: {} 22 | /******/ }; 23 | /******/ 24 | /******/ // Execute the module function 25 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 26 | /******/ 27 | /******/ // Flag the module as loaded 28 | /******/ module.l = true; 29 | /******/ 30 | /******/ // Return the exports of the module 31 | /******/ return module.exports; 32 | /******/ } 33 | /******/ 34 | /******/ 35 | /******/ // expose the modules object (__webpack_modules__) 36 | /******/ __webpack_require__.m = modules; 37 | /******/ 38 | /******/ // expose the module cache 39 | /******/ __webpack_require__.c = installedModules; 40 | /******/ 41 | /******/ // define getter function for harmony exports 42 | /******/ __webpack_require__.d = function(exports, name, getter) { 43 | /******/ if(!__webpack_require__.o(exports, name)) { 44 | /******/ Object.defineProperty(exports, name, { 45 | /******/ configurable: false, 46 | /******/ enumerable: true, 47 | /******/ get: getter 48 | /******/ }); 49 | /******/ } 50 | /******/ }; 51 | /******/ 52 | /******/ // getDefaultExport function for compatibility with non-harmony modules 53 | /******/ __webpack_require__.n = function(module) { 54 | /******/ var getter = module && module.__esModule ? 55 | /******/ function getDefault() { return module['default']; } : 56 | /******/ function getModuleExports() { return module; }; 57 | /******/ __webpack_require__.d(getter, 'a', getter); 58 | /******/ return getter; 59 | /******/ }; 60 | /******/ 61 | /******/ // Object.prototype.hasOwnProperty.call 62 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 63 | /******/ 64 | /******/ // __webpack_public_path__ 65 | /******/ __webpack_require__.p = ""; 66 | /******/ 67 | /******/ // Load entry module and return exports 68 | /******/ return __webpack_require__(__webpack_require__.s = 2); 69 | /******/ }) 70 | /************************************************************************/ 71 | /******/ ([ 72 | /* 0 */ 73 | /***/ (function(module, exports) { 74 | 75 | module.exports = require("sketch/dom"); 76 | 77 | /***/ }), 78 | /* 1 */ 79 | /***/ (function(module, exports) { 80 | 81 | module.exports = require("sketch"); 82 | 83 | /***/ }), 84 | /* 2 */ 85 | /***/ (function(module, exports, __webpack_require__) { 86 | 87 | "use strict"; 88 | 89 | 90 | var sketch = __webpack_require__(1); 91 | var Artboard = sketch.Artboard; 92 | var Text = __webpack_require__(1).Text; 93 | var ShapePath = __webpack_require__(0).ShapePath; 94 | var Group = __webpack_require__(0).Group; 95 | 96 | var document = sketch.getSelectedDocument(); 97 | var page = document.selectedPage; 98 | var docSwatches = document.swatches; 99 | var imported_swatches = []; 100 | 101 | var selection = context.selection; 102 | var fromTheme, toTheme; 103 | var currentArtboard; 104 | var newArtboardName; 105 | var instance; 106 | var group_name; 107 | 108 | var move = 0; 109 | var master_iterator = -1; 110 | 111 | var CreateArtboard = true; 112 | var TextLibrary = void 0; 113 | var ColourLibrary = void 0; 114 | 115 | var isGlobalColour = false; 116 | var isGlobalText = false; 117 | 118 | var myArtboard; 119 | 120 | var layerstyleReferences; 121 | var textstyleReferences; 122 | var swatchRefs = void 0; 123 | 124 | function removeArtboardNames() { 125 | 126 | //removes current artboard naming 127 | 128 | if (newArtboardName.includes("Dark")) { 129 | newArtboardName = newArtboardName.replace('Dark', ''); 130 | } else if (newArtboardName.includes("Light")) { 131 | newArtboardName = newArtboardName.replace('Light', ''); 132 | } 133 | 134 | if (newArtboardName.includes("dark")) { 135 | newArtboardName = newArtboardName.replace('dark', ''); 136 | } else if (newArtboardName.includes("light")) { 137 | newArtboardName = newArtboardName.replace('light', ''); 138 | } 139 | 140 | if (newArtboardName.includes("dakr")) { 141 | newArtboardName = newArtboardName.replace('dakr', ''); 142 | } else if (newArtboardName.includes("lihgt")) { 143 | newArtboardName = newArtboardName.replace('lihgt', ''); 144 | } 145 | } 146 | 147 | function createArtboatd() { 148 | if (CreateArtboard) { 149 | myArtboard = new Artboard({ 150 | parent: page, 151 | name: newArtboardName + ' ' + toTheme, 152 | frame: { 153 | x: currentArtboard.frame.x + currentArtboard.frame.width + move, 154 | y: currentArtboard.frame.y, 155 | width: currentArtboard.frame.width, 156 | height: currentArtboard.frame.height 157 | } 158 | }); 159 | 160 | myArtboard.background.enabled = true; 161 | 162 | if (toTheme == "Dark") { 163 | myArtboard.background.color = '#000000ff'; 164 | } 165 | CreateArtboard = false; 166 | } 167 | } 168 | 169 | var libraries = __webpack_require__(0).getLibraries(); 170 | for (var l = 0; l < Object.keys(libraries).length; l++) { 171 | if (libraries[l].name.includes('Type') || libraries[l].name.includes('Text') || libraries[l].name.includes('Fonts') || libraries[l].name.includes('Fundamentals')) { 172 | isGlobalText = true; 173 | TextLibrary = sketch.getLibraries()[l]; 174 | textstyleReferences = TextLibrary.getImportableTextStyleReferencesForDocument(document); 175 | } 176 | 177 | if (libraries[l].name.includes('Color') || libraries[l].name.includes('Fundamentals') || libraries[l].name.includes('Colours')) { 178 | isGlobalColour = true; 179 | ColourLibrary = sketch.getLibraries()[l]; 180 | layerstyleReferences = ColourLibrary.getImportableLayerStyleReferencesForDocument(document); 181 | swatchRefs = ColourLibrary.getImportableSwatchReferencesForDocument(document); 182 | } 183 | } 184 | 185 | //for each layer selected in the document, checking if there are artboards with sublayers 186 | 187 | 188 | var UI = __webpack_require__(3); 189 | 190 | UI.getInputFromUser("Switch to", { 191 | type: UI.INPUT_TYPE.selection, 192 | possibleValues: ['Light', 'Dark'] 193 | }, function (err, value) { 194 | if (err) { 195 | UI.close(); 196 | return; 197 | } 198 | 199 | if (value == 'Light') { 200 | fromTheme = 'Dark'; 201 | toTheme = 'Light'; 202 | } 203 | if (value == 'Dark') { 204 | fromTheme = 'Light'; 205 | toTheme = 'Dark'; 206 | } 207 | }); 208 | 209 | selection.forEach(function (layer) { 210 | if (layer.layers && layer['class']() == "MSArtboardGroup") { 211 | master_iterator++;move += 150; 212 | var children = layer.children(); 213 | currentArtboard = sketch.getSelectedDocument().selectedLayers.layers[master_iterator]; 214 | 215 | //artboard name handling 216 | 217 | newArtboardName = sketch.getSelectedDocument().selectedLayers.layers[master_iterator].name; 218 | removeArtboardNames(); 219 | 220 | CreateArtboard = true; 221 | createArtboatd(); 222 | 223 | //////////GETTING ALL LAYERS IN AN ARTBOARD BY NAME, THEN ID 224 | 225 | for (var f = 0; f < children.count(); f++) { 226 | if (children[f]['class']() != "MSArtboardGroup") { 227 | var extracted_children_id = children[f].toString().split('(').pop().split(')')[0]; 228 | var current_layer_in_artboard = document.getLayerWithID(extracted_children_id); 229 | 230 | //////////SYMBOL LAYER 231 | 232 | if (current_layer_in_artboard.type == "SymbolInstance") { 233 | 234 | //I use the extracted ID and found layer to match the correct library & master references 235 | 236 | var library_linked = current_layer_in_artboard.master.getLibrary(); 237 | var symbolReferences = library_linked.getImportableSymbolReferencesForDocument(document); 238 | var _swatchRefs = library_linked.getImportableSwatchReferencesForDocument(document); 239 | 240 | if (!isGlobalColour) { 241 | layerstyleReferences = library_linked.getImportableLayerStyleReferencesForDocument(document); 242 | } 243 | if (!isGlobalText) { 244 | textstyleReferences = library_linked.getImportableTextStyleReferencesForDocument(document); 245 | } 246 | 247 | symbolReferences.forEach(function (ImportableObject) { 248 | if (ImportableObject.id == current_layer_in_artboard.symbolId) { 249 | var imported_symbol_name = ImportableObject.name.replace(fromTheme, toTheme); 250 | var to_Import = symbolReferences.filter(function (element) { 251 | return element.name == imported_symbol_name; 252 | }); 253 | var symbolMaster = to_Import[0]['import'](); 254 | instance = symbolMaster.createNewInstance(); 255 | 256 | //sizes checked to match symbol to existing instance 257 | 258 | var new_frame = current_layer_in_artboard.frame; 259 | var new_x = new_frame.x; 260 | var new_y = new_frame.y; 261 | var new_width = new_frame.width; 262 | var new_height = new_frame.height; 263 | var new_rotation = current_layer_in_artboard.transform.rotation; 264 | var new_transform = current_layer_in_artboard.transform; 265 | var new_instance_name = current_layer_in_artboard.name; 266 | 267 | instance.name = new_instance_name; 268 | instance.transform = new_transform; 269 | instance.transform.rotation = new_rotation; 270 | instance.frame = { x: new_x, y: new_y, width: new_width, height: new_height }; 271 | instance.parent = myArtboard; 272 | 273 | //going through overrides for symbols and texts separately 274 | 275 | for (var w = 0; w < Object.keys(instance.overrides).length; w++) { 276 | 277 | //only get the override if the value is differnet from the default one, otherwise let the symbol handle it 278 | 279 | //looking at overriding text 280 | if (current_layer_in_artboard.overrides[w].property == 'stringValue' && !current_layer_in_artboard.overrides[w].isDefault) { 281 | instance.overrides[w].value = current_layer_in_artboard.overrides[w].value; 282 | } 283 | 284 | if (current_layer_in_artboard.overrides[w].property == 'layerStyle' && !current_layer_in_artboard.overrides[w].isDefault) { 285 | layerstyleReferences.forEach(function (element) { 286 | if (element.id == current_layer_in_artboard.overrides[w].value) { 287 | var layer_style_switch_name = element.name.replace(fromTheme, toTheme); 288 | layerstyleReferences.forEach(function (sub_element) { 289 | 290 | if (sub_element.name == layer_style_switch_name) { 291 | 292 | var sharedStyle = sub_element['import'](); 293 | instance.overrides[w].value = sharedStyle.id; 294 | } 295 | }); 296 | } 297 | }); 298 | } 299 | 300 | if (current_layer_in_artboard.overrides[w].property == 'textStyle' && !current_layer_in_artboard.overrides[w].isDefault) { 301 | 302 | textstyleReferences.forEach(function (element) { 303 | 304 | if (element.id == current_layer_in_artboard.overrides[w].value) { 305 | var text_stlye_override_switch_name = element.name.replace(fromTheme, toTheme); 306 | //look for the switched text style by its name 307 | 308 | textstyleReferences.forEach(function (sub_element) { 309 | if (sub_element.name == text_stlye_override_switch_name) { 310 | var textStyle_to_import = sub_element['import'](); 311 | instance.overrides[w].value = textStyle_to_import.id; 312 | } 313 | }); 314 | } 315 | }); 316 | } 317 | 318 | if (current_layer_in_artboard.overrides[w].property == 'symbolID' && !current_layer_in_artboard.overrides[w].isDefault) { 319 | var override_symbol_master = document.getSymbolMasterWithID(current_layer_in_artboard.overrides[w].value); 320 | var override_library_linked = override_symbol_master.getLibrary(); 321 | 322 | var override_symbolReferences = override_library_linked.getImportableSymbolReferencesForDocument(document); 323 | 324 | override_symbolReferences.forEach(function (element) { 325 | 326 | if (element.id == current_layer_in_artboard.overrides[w].value) { 327 | 328 | var symbol_override_switch_name = element.name.replace(fromTheme, toTheme); 329 | //look for the switched symbol by its name 330 | 331 | override_symbolReferences.forEach(function (sub_element) { 332 | if (sub_element.name == symbol_override_switch_name) { 333 | 334 | //importing the same symbol into the artboard , deleting it afterward to have it as a reference 335 | 336 | var master_to_delte = sub_element['import'](); 337 | var instance_to_delete = master_to_delte.createNewInstance(); 338 | 339 | instance_to_delete.parent = myArtboard; 340 | instance.overrides[w].value = instance_to_delete.symbolId; 341 | instance_to_delete.remove(); 342 | } 343 | }); 344 | } 345 | }); 346 | } 347 | } 348 | } 349 | }); 350 | } 351 | 352 | //////////TEXT LAYER 353 | 354 | if (current_layer_in_artboard.type == 'Text') { 355 | var text = new Text({ 356 | text: current_layer_in_artboard.text, 357 | frame: current_layer_in_artboard.frame, 358 | style: current_layer_in_artboard.style, 359 | name: current_layer_in_artboard.name, 360 | sharedStyleId: current_layer_in_artboard.sharedStyleId, 361 | sharedStyle: current_layer_in_artboard.sharedStyle 362 | }); 363 | text.behaviour = current_layer_in_artboard.behaviour; 364 | text.parent = myArtboard; 365 | 366 | if (text.sharedStyle != null) { 367 | if (text.sharedStyle.name.includes(fromTheme)) { 368 | var switch_text_style = text.sharedStyle.name.replace(fromTheme, toTheme); 369 | 370 | textstyleReferences.forEach(function (element) { 371 | if (element.name == switch_text_style) { 372 | var new_text_style = element['import'](); 373 | text.sharedStyle = new_text_style; 374 | text.style.syncWithSharedStyle(new_text_style); 375 | } 376 | }); 377 | } 378 | } else { 379 | //there are no text styles, just local swatches used 380 | 381 | if (!isGlobalColour) { 382 | log("Looking into local swatches for text colours"); 383 | log("Current text color" + text.style.textColor); 384 | docSwatches.forEach(function (element) { 385 | if (element.color == text.style.textColor) { 386 | var swatch_switch = element.name; 387 | swatch_switch = swatch_switch.replace(fromTheme, toTheme); 388 | docSwatches.forEach(function (sub_swatch) { 389 | if (sub_swatch.name == swatch_switch) { 390 | text.style.textColor = sub_swatch.referencingColor; 391 | } 392 | }); 393 | } 394 | }); 395 | } 396 | 397 | //No text styles, global swatches are used 398 | else { 399 | var global_swatch_switch_text; 400 | log("Looking into global swatches"); 401 | 402 | //importing all swatches into an array to get colours and names 403 | 404 | swatchRefs.forEach(function (element) { 405 | imported_swatches.push(element['import']()); 406 | }); 407 | /*finding which swatch is used currently based on color code */ 408 | imported_swatches.forEach(function (sub_element) { 409 | if (sub_element.color == text.style.textColor) { 410 | log("Current swatch is" + sub_element.name); 411 | 412 | global_swatch_switch_text = sub_element.name; 413 | global_swatch_switch_text = global_swatch_switch_text.replace(fromTheme, toTheme); 414 | } 415 | }); 416 | /*finding out the alternative darl/light swatch, then replacing it for the shape */ 417 | imported_swatches.forEach(function (sub_sub_element) { 418 | if (sub_sub_element.name == global_swatch_switch_text) { 419 | text.style.textColor = sub_sub_element.referencingColor; 420 | } 421 | }); 422 | } 423 | } 424 | } 425 | 426 | //////////SHAPE LAYER 427 | 428 | if (current_layer_in_artboard.type == 'ShapePath') { 429 | var global_swatch_switch; 430 | 431 | (function () { 432 | var shapePath = new ShapePath({ 433 | shapeType: current_layer_in_artboard.shapeType, 434 | sharedStyle: current_layer_in_artboard.sharedStyle, 435 | frame: current_layer_in_artboard.frame, 436 | points: current_layer_in_artboard.points, 437 | transform: current_layer_in_artboard.transform, 438 | name: current_layer_in_artboard.name, 439 | style: current_layer_in_artboard.style, 440 | parent: myArtboard 441 | 442 | }); 443 | shapePath.transform.rotation = current_layer_in_artboard.transform.rotation; 444 | 445 | if (shapePath.sharedStyle != null) { 446 | //layerstyleReferences only looks at layerstyles imported from the previous symbol in the document. 447 | layerstyleReferences.forEach(function (element) { 448 | if (element.id == shapePath.sharedStyleId) { 449 | 450 | var switch_layer_style = element.name; 451 | switch_layer_style = switch_layer_style.replace(fromTheme, toTheme); 452 | 453 | layerstyleReferences.forEach(function (sub_element) { 454 | if (sub_element.name == switch_layer_style) { 455 | var new_new_layer_style = sub_element['import'](); 456 | shapePath.sharedStyleId = new_new_layer_style.id; 457 | shapePath.style.syncWithSharedStyle(new_new_layer_style); 458 | } 459 | }); 460 | } 461 | }); 462 | } else { 463 | //shapes using colour variables/swatches. Check if swatches are in a Colour global library or not 464 | 465 | if (!isGlobalColour) { 466 | log("Looking into local swatches"); 467 | docSwatches.forEach(function (element) { 468 | if (element.color == shapePath.style.fills[0].color) { 469 | var swatch_switch = element.name; 470 | swatch_switch = swatch_switch.replace(fromTheme, toTheme); 471 | docSwatches.forEach(function (sub_swatch) { 472 | if (sub_swatch.name == swatch_switch) { 473 | shapePath.style.fills[0].color = sub_swatch.referencingColor; 474 | } 475 | }); 476 | } 477 | }); 478 | } else { 479 | log("Looking into global swatches"); 480 | 481 | //importing all swatches into an array to get colours and names 482 | 483 | swatchRefs.forEach(function (element) { 484 | imported_swatches.push(element['import']()); 485 | }); 486 | /*finding which swatch is used currently based on color code */ 487 | imported_swatches.forEach(function (sub_element) { 488 | if (sub_element.color == shapePath.style.fills[0].color) { 489 | log("Current swatch is" + sub_element.name); 490 | 491 | global_swatch_switch = sub_element.name; 492 | global_swatch_switch = global_swatch_switch.replace(fromTheme, toTheme); 493 | } 494 | }); 495 | /*finding out the alternative darl/light swatch, then replacing it for the shape */ 496 | imported_swatches.forEach(function (sub_sub_element) { 497 | if (sub_sub_element.name == global_swatch_switch) { 498 | shapePath.style.fills[0].color = sub_sub_element.referencingColor; 499 | } 500 | }); 501 | } 502 | } 503 | })(); 504 | } 505 | } 506 | } 507 | } else { 508 | UI.message('Please select one or more artboards'); 509 | } 510 | }); 511 | 512 | /***/ }), 513 | /* 3 */ 514 | /***/ (function(module, exports) { 515 | 516 | module.exports = require("sketch/ui"); 517 | 518 | /***/ }) 519 | /******/ ]); 520 | if (key === 'default' && typeof exports === 'function') { 521 | exports(context); 522 | } else { 523 | exports[key](context); 524 | } 525 | } 526 | that['onRun'] = __skpm_run.bind(this, 'default') 527 | --------------------------------------------------------------------------------