diff --git a/src/css/font-icon.css b/src/css/font-icon.css index ec44f24a..f771603a 100644 --- a/src/css/font-icon.css +++ b/src/css/font-icon.css @@ -1,10 +1,10 @@ @font-face { font-family: 'piskel'; - src:url('fonts/piskel.eot?-3olv93'); - src:url('fonts/piskel.eot?#iefix-3olv93') format('embedded-opentype'), - url('fonts/piskel.woff?-3olv93') format('woff'), - url('fonts/piskel.ttf?-3olv93') format('truetype'), - url('fonts/piskel.svg?-3olv93#icomoon') format('svg'); + src:url('fonts/icomoon.eot?-3olv93'); + src:url('fonts/icomoon.eot?#iefix-3olv93') format('embedded-opentype'), + url('fonts/icomoon.woff?-3olv93') format('woff'), + url('fonts/icomoon.ttf?-3olv93') format('truetype'), + url('fonts/icomoon.svg?-3olv93#icomoon') format('svg'); font-weight: normal; font-style: normal; } @@ -31,3 +31,66 @@ content: "\e601"; } +.piskel-icon-download:before { + content: "\e600"; +} + +.piskel-icon-rotateleft:before { + content: "\e603"; +} + +.piskel-icon-rotateright:before { + content: "\e604"; +} + +.piskel-icon-fliph:before { + content: "\e605"; +} + +.piskel-icon-flipv:before { + content: "\e606"; +} + +.piskel-icon-trashplain:before { + content: "\e607"; +} + +.piskel-icon-trash:before { + content: "\e608"; +} + +.piskel-icon-merge:before { + content: "\e609"; +} + +.piskel-icon-pencil:before { + content: "\e610"; +} + +.piskel-icon-close:before { + content: "\e611"; +} + +.piskel-icon-minus:before { + content: "\e60a"; +} + +.piskel-icon-plus:before { + content: "\e60b"; +} + +.piskel-icon-arrow-up-fat:before { + content: "\e60c"; +} + +.piskel-icon-arrow-down-fat:before { + content: "\e60d"; +} + +.piskel-icon-arrow-up-thin:before { + content: "\e60e"; +} + +.piskel-icon-arrow-down-thin:before { + content: "\e60f"; +} diff --git a/src/css/fonts/icomoon.eot b/src/css/fonts/icomoon.eot new file mode 100644 index 00000000..f5314adf Binary files /dev/null and b/src/css/fonts/icomoon.eot differ diff --git a/src/css/fonts/icomoon.svg b/src/css/fonts/icomoon.svg new file mode 100644 index 00000000..bafc0fee --- /dev/null +++ b/src/css/fonts/icomoon.svg @@ -0,0 +1,28 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/css/fonts/icomoon.ttf b/src/css/fonts/icomoon.ttf new file mode 100644 index 00000000..5ae72b63 Binary files /dev/null and b/src/css/fonts/icomoon.ttf differ diff --git a/src/css/fonts/icomoon.woff b/src/css/fonts/icomoon.woff new file mode 100644 index 00000000..a9d1e6c2 Binary files /dev/null and b/src/css/fonts/icomoon.woff differ diff --git a/src/css/toolbox-layers-list.css b/src/css/toolbox-layers-list.css index 61a96d88..116644f8 100644 --- a/src/css/toolbox-layers-list.css +++ b/src/css/toolbox-layers-list.css @@ -77,11 +77,6 @@ color: gold; } -.layers-button-arrow { - font-family : 'Lucida Grande', Calibri; - padding : 2px 6px 0 6px; -} - .layers-button-container { overflow : hidden; } diff --git a/src/css/toolbox-palettes-list.css b/src/css/toolbox-palettes-list.css index a1db618a..e77e10ad 100644 --- a/src/css/toolbox-palettes-list.css +++ b/src/css/toolbox-palettes-list.css @@ -1,102 +1,106 @@ -.palettes-list-select { - max-width:120px; - margin-top: 3px; -} - .palettes-title { - background-size: 22px; - background-repeat: no-repeat; - background-position: 97%; + background-size: 22px; + background-repeat: no-repeat; + background-position: 97%; } - .palettes-list-colors { - overflow: auto; - max-height: 160px; + overflow: auto; + max-height: 160px; } - .palettes-list-color { - cursor : pointer; - float: left; - margin : 0 0 5px 5px; - width : 32px; - height : 32px; - position: relative; + cursor: pointer; + float: left; + margin: 0 0 5px 5px; + width: 32px; + height: 32px; + position: relative; } - .palettes-list-color:nth-child(-n+5) { - margin-top: 5px; + margin-top: 5px; } - -.palettes-list-color div{ - width : 32px; - height : 32px; +.palettes-list-color div { + width: 32px; + height: 32px; } - -.palettes-list-has-scrollbar .palettes-list-color, -.palettes-list-has-scrollbar .palettes-list-color div{ - width: 29px +.palettes-list-has-scrollbar .palettes-list-color, .palettes-list-has-scrollbar .palettes-list-color div { + width: 29px } - -.palettes-list-primary-color:before, -.palettes-list-secondary-color:before { - content: ""; - position: absolute; - bottom: 1px; - display: inline-block; - border: 7px solid gold; - border-top-color: transparent; - - width: 0px; - height: 0px; +.palettes-list-primary-color:before, .palettes-list-secondary-color:before { + content: ""; + position: absolute; + bottom: 1px; + display: inline-block; + border: 7px solid gold; + border-top-color: transparent; + width: 0px; + height: 0px; } - .palettes-list-primary-color:before { - left: 1px; - border-right-color: transparent; + left: 1px; + border-right-color: transparent; } - .palettes-list-secondary-color:before { - right: 1px; - border-left-color: transparent; + right: 1px; + border-left-color: transparent; +} +.palettes-list-actions { + background-color: #3f3f3f; + border-bottom-color: #222; + height: 24px; + padding: 0; + overflow: hidden; } -.palettes-list-actions { - background-color: #3f3f3f; - border-bottom-color: #222; - height: 24px; - padding: 0; - overflow: hidden; +.palettes-list-button, +.palettes-list-select { + margin: 0; + float: left; } .palettes-list-button { - margin: 0; - width: 36px; - float: left; + width: 16.66667%; +} +.palettes-list-select { + width: 66.66667%; + height: 100%; + padding: 0 5px 0 5px; + + border-style: solid; + border-width: 1px 0 1px 0; + + color: #aaa; + font-size : 0.75em; + + /*thanks firefox, you suck*/ + text-align:left; + /*text-shadow:none;*/ + font-weight: normal; + + transition : background-color 0.3s, color 0.3s; + cursor:pointer; } -.palettes-list-select { - float: left; - width: 66.66667%; - height: 100%; - - margin: 0; - padding: 0 5px 0 5px; - - background: #3f3f3f; - - border-style : solid; - border-top-color: #666; - border-bottom-color: #222; - border-width: 1px 0 1px 0; - - color: white; +.palettes-list-select:hover { + color: white; + background-color: #484848; } .palettes-list-select:focus { - outline:none; + background-color: #484848; + color: white; + outline: none; } .palettes-list-actions .edit-icon { - background-size : 15px; - background-position : 50%; -} \ No newline at end of file + background-size: 15px; + background-position: 50%; +} +.palettes-list-no-colors { + height: 42px; + width: 100%; + color: grey; + font-size: 0.7em; + font-style: italic; + line-height: 42px; + text-align: center +} diff --git a/src/js/controller/LayersListController.js b/src/js/controller/LayersListController.js index b33c36a3..44bf10d3 100644 --- a/src/js/controller/LayersListController.js +++ b/src/js/controller/LayersListController.js @@ -28,6 +28,31 @@ this.layersListEl.innerHTML = ''; var layers = this.piskelController.getLayers(); layers.forEach(this.addLayerItem.bind(this)); + this.updateButtonStatus_(); + }; + + ns.LayersListController.prototype.updateButtonStatus_ = function () { + var layers = this.piskelController.getLayers(); + var currentLayer = this.piskelController.getCurrentLayer(); + var index = this.piskelController.getCurrentLayerIndex(); + + var isLast = index === 0; + var isOnly = layers.length === 1; + var isFirst = index === layers.length - 1; + + this.toggleButtonDisabledState_('up', isFirst); + this.toggleButtonDisabledState_('down', isLast); + this.toggleButtonDisabledState_('merge', isLast); + this.toggleButtonDisabledState_('delete', isOnly); + }; + + ns.LayersListController.prototype.toggleButtonDisabledState_ = function (buttonAction, isDisabled) { + var button = document.querySelector('.layers-button[data-action="'+buttonAction+'"]'); + if (isDisabled) { + button.setAttribute('disabled', 'disabled'); + } else { + button.removeAttribute('disabled'); + } }; ns.LayersListController.prototype.updateToggleLayerPreview_ = function () { @@ -64,25 +89,21 @@ } else if (el.classList.contains('layer-item')) { index = el.dataset.layerIndex; this.piskelController.setCurrentLayerIndex(parseInt(index, 10)); - } else if (el.classList.contains('edit-icon')) { - index = el.parentNode.dataset.layerIndex; - this.renameLayerAt_(index); - } else if (el.classList.contains('merge-icon')) { - index = el.parentNode.dataset.layerIndex; - this.mergeDownLayerAt_(index); } }; - ns.LayersListController.prototype.renameLayerAt_ = function (index) { - var layer = this.piskelController.getLayerAt(index); + ns.LayersListController.prototype.renameCurrentLayer_ = function () { + var layer = this.piskelController.getCurrentLayer(); var name = window.prompt("Please enter the layer name", layer.getName()); if (name) { + var index = this.piskelController.getCurrentLayerIndex(); this.piskelController.renameLayerAt(index, name); this.renderLayerList_(); } }; - ns.LayersListController.prototype.mergeDownLayerAt_ = function (index) { + ns.LayersListController.prototype.mergeDownCurrentLayer_ = function () { + var index = this.piskelController.getCurrentLayerIndex(); this.piskelController.mergeDownLayerAt(index); this.renderLayerList_(); }; @@ -97,6 +118,10 @@ this.piskelController.createLayer(); } else if (action == 'delete') { this.piskelController.removeCurrentLayer(); + } else if (action == 'merge') { + this.mergeDownCurrentLayer_(); + } else if (action == 'edit') { + this.renameCurrentLayer_(); } }; diff --git a/src/js/controller/PalettesListController.js b/src/js/controller/PalettesListController.js index 82c50180..3a848256 100644 --- a/src/js/controller/PalettesListController.js +++ b/src/js/controller/PalettesListController.js @@ -54,20 +54,25 @@ }; ns.PalettesListController.prototype.fillColorListContainer = function () { + var colors = this.getSelectedPaletteColors_(); - var html = colors.map(function (color) { - return pskl.utils.Template.replace(this.paletteColorTemplate_, {color : color}); - }.bind(this)).join(''); - this.colorListContainer_.innerHTML = html; + if (colors.length > 0) { + var html = colors.map(function (color) { + return pskl.utils.Template.replace(this.paletteColorTemplate_, {color : color}); + }.bind(this)).join(''); + this.colorListContainer_.innerHTML = html; - this.highlightSelectedColors(); + this.highlightSelectedColors(); - var hasScrollbar = colors.length > NO_SCROLL_MAX_COLORS; - if (hasScrollbar && !pskl.utils.UserAgent.isChrome) { - this.colorListContainer_.classList.add(HAS_SCROLL_CLASSNAME); + var hasScrollbar = colors.length > NO_SCROLL_MAX_COLORS; + if (hasScrollbar && !pskl.utils.UserAgent.isChrome) { + this.colorListContainer_.classList.add(HAS_SCROLL_CLASSNAME); + } else { + this.colorListContainer_.classList.remove(HAS_SCROLL_CLASSNAME); + } } else { - this.colorListContainer_.classList.remove(HAS_SCROLL_CLASSNAME); + this.colorListContainer_.innerHTML = pskl.utils.Template.get('palettes-list-no-colors-partial'); } }; @@ -105,6 +110,7 @@ ns.PalettesListController.prototype.onPaletteSelected_ = function (evt) { var paletteId = this.colorPaletteSelect_.value; this.selectPalette(paletteId); + this.colorPaletteSelect_.blur(); }; ns.PalettesListController.prototype.onCreatePaletteClick_ = function (evt) { diff --git a/src/js/controller/dialogs/AbstractDialogController.js b/src/js/controller/dialogs/AbstractDialogController.js index 130d351e..296b78e9 100644 --- a/src/js/controller/dialogs/AbstractDialogController.js +++ b/src/js/controller/dialogs/AbstractDialogController.js @@ -17,9 +17,9 @@ }; ns.AbstractDialogController.prototype.setTitle = function (title) { - var dialogHead = document.querySelector('.dialog-head'); - if (dialogHead) { - dialogHead.innerText = title; + var dialogTitle = document.querySelector('.dialog-title'); + if (dialogTitle) { + dialogTitle.innerText = title; } }; diff --git a/src/js/controller/dialogs/CreatePaletteController.js b/src/js/controller/dialogs/CreatePaletteController.js index 5fff0cb3..fa777fcc 100644 --- a/src/js/controller/dialogs/CreatePaletteController.js +++ b/src/js/controller/dialogs/CreatePaletteController.js @@ -111,10 +111,16 @@ ns.CreatePaletteController.prototype.onFileInputChange_ = function (evt) { var files = this.hiddenFileInput.files; if (files.length == 1) { - this.paletteImportService.read(files[0], this.setPalette_.bind(this)); + this.paletteImportService.read(files[0], this.setPalette_.bind(this), this.displayErrorMessage_.bind(this)); } }; + ns.CreatePaletteController.prototype.displayErrorMessage_ = function (message) { + message = "Could not import palette : " + message; + $.publish(Events.SHOW_NOTIFICATION, [{"content": message}]); + window.setTimeout($.publish.bind($, Events.HIDE_NOTIFICATION), 2000); + }; + ns.CreatePaletteController.prototype.onNameInputChange_ = function (evt) { this.palette.name = pskl.utils.escapeHtml(this.nameInput.value); }; diff --git a/src/js/service/keyboard/CheatsheetService.js b/src/js/service/keyboard/CheatsheetService.js index 56818073..f2194c1e 100644 --- a/src/js/service/keyboard/CheatsheetService.js +++ b/src/js/service/keyboard/CheatsheetService.js @@ -14,8 +14,6 @@ pskl.app.shortcutService.addShortcut('shift+?', this.toggleCheatsheet_.bind(this)); pskl.app.shortcutService.addShortcut('?', this.toggleCheatsheet_.bind(this)); - document.body.addEventListener('click', this.onBodyClick_.bind(this)); - var link = $('.cheatsheet-link'); link.click(this.toggleCheatsheet_.bind(this)); @@ -24,17 +22,6 @@ $.subscribe(Events.ESCAPE, this.onEscape_.bind(this)); }; - ns.CheatsheetService.prototype.onBodyClick_ = function (event) { - if (this.isDisplayed_) { - var target = event.target; - var cheatsheetContainerEl = document.querySelector('.cheatsheet-container'); - var isInCheatsheetContainer = pskl.utils.Dom.isParent(target, cheatsheetContainerEl); - if (!isInCheatsheetContainer) { - this.hideCheatsheet_(); - } - } - }; - ns.CheatsheetService.prototype.toggleCheatsheet_ = function () { if (this.isDisplayed_) { this.hideCheatsheet_(); diff --git a/src/js/service/palette/PaletteImageReader.js b/src/js/service/palette/PaletteImageReader.js index 330bb0d7..61945904 100644 --- a/src/js/service/palette/PaletteImageReader.js +++ b/src/js/service/palette/PaletteImageReader.js @@ -12,18 +12,41 @@ }; ns.PaletteImageReader.prototype.onImageLoaded_ = function (image) { - var frame = pskl.utils.FrameUtils.createFromImage(image); - var colorsMap = {}; - frame.forEachPixel(function (color, x, y) { - colorsMap[color] = true; - }); + var imageProcessor = new pskl.worker.ImageProcessor(image, + this.onWorkerSuccess_.bind(this), + this.onWorkerStep_.bind(this), + this.onWorkerError_.bind(this)); + imageProcessor.process(); + }; - delete colorsMap[Constants.TRANSPARENT_COLOR]; + ns.PaletteImageReader.prototype.onWorkerSuccess_ = function (event) { + var data = event.data; + var colorsMap = data.colorsMap; var colors = Object.keys(colorsMap); - var uuid = pskl.utils.Uuid.generate(); - var palette = new pskl.model.Palette(uuid, this.file.name + ' palette', colors); - this.onSuccess(palette); + if (colors.length > 200) { + this.onError('Too many colors : ' + colors.length); + } else { + var uuid = pskl.utils.Uuid.generate(); + var palette = new pskl.model.Palette(uuid, this.file.name + ' palette', colors); + + this.onSuccess(palette); + } + }; + ns.PaletteImageReader.prototype.onWorkerStep_ = function (event) { + var data = event.data; + var step = data.step; + var total = data.total; + + var progress = ((step/total)*100).toFixed(1); + + if (this.currentProgress !== progress) { + this.currentProgress = progress; + console.log("Image processing completed at : " + progress + "%"); + } + }; + ns.PaletteImageReader.prototype.onWorkerError_ = function (event) { + this.onError('Unable to process the image : ' + event.data.message); }; })(); \ No newline at end of file diff --git a/src/js/utils/FrameUtils.js b/src/js/utils/FrameUtils.js index b98c2f43..4eb55b91 100644 --- a/src/js/utils/FrameUtils.js +++ b/src/js/utils/FrameUtils.js @@ -106,17 +106,24 @@ * @return {pskl.model.Frame} corresponding frame */ createFromImage : function (image) { + var w = image.width, + h = image.height; + var imgData = pskl.utils.FrameUtils.imageToImageData(image); + var grid = pskl.utils.FrameUtils.imageDataToGrid(imgData,w, h, Constants.TRANSPARENT_COLOR); + return pskl.model.Frame.fromGrid(grid); + }, + + imageToImageData : function (image) { var w = image.width, h = image.height; var canvas = pskl.CanvasUtils.createCanvas(w, h); var context = canvas.getContext('2d'); context.drawImage(image, 0,0,w,h,0,0,w,h); - var imgData = context.getImageData(0,0,w,h).data; - return pskl.utils.FrameUtils.createFromImageData(imgData, w, h); + return context.getImageData(0,0,w,h).data; }, - createFromImageData : function (imageData, width, height) { + imageDataToGrid : function (imageData, width, height, transparent) { // Draw the zoomed-up pixels to a different canvas context var grid = []; for (var x = 0 ; x < width ; x++){ @@ -129,13 +136,13 @@ var b = imageData[i+2]; var a = imageData[i+3]; if (a < 125) { - grid[x][y] = Constants.TRANSPARENT_COLOR; + grid[x][y] = transparent; } else { grid[x][y] = pskl.utils.FrameUtils.rgbToHex(r,g,b); } } } - return pskl.model.Frame.fromPixelGrid(grid); + return grid; }, /** diff --git a/src/js/worker/ImageProcessor.js b/src/js/worker/ImageProcessor.js new file mode 100644 index 00000000..9c05e986 --- /dev/null +++ b/src/js/worker/ImageProcessor.js @@ -0,0 +1,167 @@ +(function () { + var ns = $.namespace('pskl.worker'); + + var worker = function () { + + var postStep_ = function (step, total) { + this.postMessage({ + type : 'STEP', + step : step, + total : total + }); + }; + + var imageDataToGrid = function (imageData, width, height, transparent) { + // Draw the zoomed-up pixels to a different canvas context + var grid = []; + for (var x = 0 ; x < width ; x++){ + grid[x] = []; + postStep_(x, (width-1)*2); + for (var y = 0 ; y < height ; y++){ + // Find the starting index in the one-dimensional image data + var i = (y * width + x)*4; + var r = imageData[i ]; + var g = imageData[i+1]; + var b = imageData[i+2]; + var a = imageData[i+3]; + if (a < 125) { + grid[x][y] = transparent; + } else { + grid[x][y] = pskl.utils.FrameUtils.rgbToHex(r,g,b); + } + } + } + return grid; + }; + + var getColorsMapFromImageData = function (imageData, width, height) { + var grid = imageDataToGrid(imageData, width, height, 'transparent'); + + var colorsMap = {}; + for (var i = 0 ; i < grid.length ; i++) { + var step = (grid.length-1) + i; + var total = (grid.length-1)*2; + postStep_(step, total); + for (var j = 0 ; j < grid[i].length ; j++) { + var color = grid[i][j]; + if (color != 'transparent') { + colorsMap[color] = true; + } + } + } + return colorsMap; + }; + + this.onmessage = function(event) { + var data = event.data; + if (data.type === 'RUN_SCRIPT') { + this.importScripts(data.script); + } else { + try { + var colorsMap = getColorsMapFromImageData(data.imageData, data.width, data.height); + this.postMessage({ + type : 'SUCCESS', + colorsMap : colorsMap + }); + } catch(e) { + this.postMessage({ + type : 'ERROR', + message : e.message + }); + } + } + }; + }; + + try { + // create worker from blob + var typedArray = [(worker+"").replace(/function \(\) \{/,"").replace(/\}[^}]*$/, "")]; + var blob = new Blob(typedArray, {type: "application/javascript"}); // pass a useful mime type here + var blobUrl = window.URL.createObjectURL(blob); + } catch (e) { + console.error("Could not create worker", e.message); + } + + ns.ImageProcessor = function (image, onSuccess, onStep, onError) { + this.image = image; + + this.onStep = onStep; + this.onSuccess = onSuccess; + this.onError = onError; + + this.worker = new Worker(blobUrl); + this.worker.onmessage = this.onWorkerMessage.bind(this); + }; + + ns.ImageProcessor.prototype.process = function () { + this.importAll(pskl.utils.FrameUtils, 'pskl.utils.FrameUtils'); + this.importAll(pskl.utils.CanvasUtils, 'pskl.utils.CanvasUtils'); + + var imageData = pskl.utils.FrameUtils.imageToImageData(this.image); + this.worker.postMessage({ + imageData : imageData, + width : this.image.width, + height : this.image.height + }); + }; + + ns.ImageProcessor.prototype.createNamespace = function (name) { + var createNamespace = (function () { + var parts = name.split('.'); + if (parts.length > 0) { + var node = this; + for (var i = 0 ; i < parts.length ; i++) { + if (!node[parts[i]]) { + node[parts[i]] = {}; + } + node = node[parts[i]]; + } + } + }); + var script = createNamespace + ""; + script = script.replace(/function \(\) \{/,"").replace(/\}[^}]*$/, ""); + script = "var name = '" + name + "';" + script; + + this.runScript(script); + }; + + ns.ImageProcessor.prototype.importAll = function (classToImport, classpath) { + this.createNamespace('pskl.utils.FrameUtils'); + for (var key in classToImport) { + if (classToImport.hasOwnProperty(key)) { + this.addMethod(classToImport[key], classpath + '.' + key); + } + } + }; + + ns.ImageProcessor.prototype.addMethod = function (method, name) { + this.runScript(name + "=" + method); + }; + + ns.ImageProcessor.prototype.runScript = function (script) { + this.worker.postMessage({ + type : 'RUN_SCRIPT', + script : this.getScriptAsUrl(script) + }); + }; + + ns.ImageProcessor.prototype.getScriptAsUrl = function (script) { + var blob = new Blob([script], {type: "application/javascript"}); // pass a useful mime type here + return window.URL.createObjectURL(blob); + }; + + ns.ImageProcessor.prototype.onWorkerMessage = function (event) { + if (event.data.type === 'STEP') { + this.onStep(event); + } else if (event.data.type === 'SUCCESS') { + this.onSuccess(event); + this.worker.terminate(); + } else if (event.data.type === 'ERROR') { + this.onError(event); + this.worker.terminate(); + } + }; +})(); + + + diff --git a/src/piskel-script-list.js b/src/piskel-script-list.js index 5d0ea962..fed83252 100644 --- a/src/piskel-script-list.js +++ b/src/piskel-script-list.js @@ -163,6 +163,9 @@ "js/devtools/TestRecordController.js", "js/devtools/init.js", + // Workers + "js/worker/ImageProcessor.js", + // Application controller and initialization "js/app.js", // Bonus features !! diff --git a/src/templates/dialogs/create-palette.html b/src/templates/dialogs/create-palette.html index c9908913..a644f98a 100644 --- a/src/templates/dialogs/create-palette.html +++ b/src/templates/dialogs/create-palette.html @@ -1,6 +1,6 @@

- Create palette + Create palette X

diff --git a/src/templates/layers-list.html b/src/templates/layers-list.html index 70389fc5..8930f9f4 100644 --- a/src/templates/layers-list.html +++ b/src/templates/layers-list.html @@ -6,24 +6,35 @@ class="layers-toggle-preview piskel-icon-eye">
- - - - - - + + + + + + + + + + +
- + + + -
diff --git a/src/templates/palettes-list.html b/src/templates/palettes-list.html index c68d03f2..2dc7dded 100644 --- a/src/templates/palettes-list.html +++ b/src/templates/palettes-list.html @@ -5,11 +5,11 @@
- + class="button palettes-list-button create-palette-button piskel-icon-plus" data-action="add" + title="Create a new palette" rel="tooltip" data-placement="top" > +
@@ -18,6 +18,13 @@
+ + +