diff --git a/package-lock.json b/package-lock.json index 0dd9bb5..90dada8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,9 @@ "typescript": "^4.8.2", "web-vitals": "^2.1.4", "zustand": "^4.5.0" + }, + "devDependencies": { + "daisyui": "^4.7.2" } }, "node_modules/@adobe/css-tools": { @@ -6340,6 +6343,16 @@ "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" }, + "node_modules/css-selector-tokenizer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz", + "integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, "node_modules/css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -6531,6 +6544,34 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==" }, + "node_modules/culori": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/culori/-/culori-3.3.0.tgz", + "integrity": "sha512-pHJg+jbuFsCjz9iclQBqyL3B2HLCBF71BwVNujUYEvCeQMvV97R59MNK3R2+jgJ3a1fcZgI9B3vYgz8lzr/BFQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/daisyui": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.7.2.tgz", + "integrity": "sha512-9UCss12Zmyk/22u+JbkVrHHxOzFOyY17HuqP5LeswI4hclbj6qbjJTovdj2zRy8cCH6/n6Wh0lTLjriGnyGh0g==", + "dev": true, + "dependencies": { + "css-selector-tokenizer": "^0.8", + "culori": "^3", + "picocolors": "^1", + "postcss-js": "^4" + }, + "engines": { + "node": ">=16.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/daisyui" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -8123,6 +8164,12 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, + "node_modules/fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, "node_modules/fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -25799,6 +25846,16 @@ "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" }, + "css-selector-tokenizer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz", + "integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, "css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -25941,6 +25998,24 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==" }, + "culori": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/culori/-/culori-3.3.0.tgz", + "integrity": "sha512-pHJg+jbuFsCjz9iclQBqyL3B2HLCBF71BwVNujUYEvCeQMvV97R59MNK3R2+jgJ3a1fcZgI9B3vYgz8lzr/BFQ==", + "dev": true + }, + "daisyui": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.7.2.tgz", + "integrity": "sha512-9UCss12Zmyk/22u+JbkVrHHxOzFOyY17HuqP5LeswI4hclbj6qbjJTovdj2zRy8cCH6/n6Wh0lTLjriGnyGh0g==", + "dev": true, + "requires": { + "css-selector-tokenizer": "^0.8", + "culori": "^3", + "picocolors": "^1", + "postcss-js": "^4" + } + }, "damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -27140,6 +27215,12 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, "fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", diff --git a/package.json b/package.json index 1b23d66..c254056 100644 --- a/package.json +++ b/package.json @@ -65,5 +65,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "daisyui": "^4.7.2" } } diff --git a/roadmap.md b/roadmap.md index 9f46484..00b78db 100644 --- a/roadmap.md +++ b/roadmap.md @@ -1,41 +1,17 @@ # Doing -- wand selection -- tilemap mode +- create modal for import +- import/export + - Asprites export settings +- box selection tools # Bugs -- issues with resizing the canvas (active layer) -- local saves arnt showing up +- animation preview not showing all layers # Pre-Launch -- box selection tools - - laso selection - undo / redo -- import/export - - Asprites export settings - - import png/jpg as a layer - - import project file - - export selected frame - - all - - export selected layer - - all - - upscale/downscale exports - - save as project file - - recommend local saved projects - - delete local saved projects - - paste reference image url - -- previous 2 color switch - - just as a quick reference, does no effect the pallete - - colors used in pallette - - when changing color in pallete, update all instances of that color in all layers - -- light/dark theme - -- minimize UI - - site domain # Launch @@ -44,6 +20,10 @@ - payment page # Stretch +- previous 2 color switch +- light/dark theme +- minimize UI +- node based editor? - themes, grid colors - you can theme it - custom layout, custom shortcut @@ -79,6 +59,7 @@ - recommend colors based on current pallete and AI - recommend better color alternatives based on AI - pallette + - when changing color in pallete, update all instances of that color in all layers - import/paste and export/copy a color pallette - import pallette from image - limit pallette to x colors diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index bc0154c..aaa0bd1 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -16,10 +16,10 @@ export function Modal(props: IProps) {
props.setIsOpen(false)}> -
{ e.preventDefault(); e.stopPropagation(); }}> diff --git a/src/pages/Home/Canvas.tsx b/src/pages/Home/Canvas.tsx index e93599c..8d7b800 100644 --- a/src/pages/Home/Canvas.tsx +++ b/src/pages/Home/Canvas.tsx @@ -4,10 +4,11 @@ import ReactTooltip from 'react-tooltip'; import { IMouseState } from "../../types"; import { FaMap } from "react-icons/fa6"; import { BiPlusMedical } from "react-icons/bi"; +import { BsCircleHalf } from "react-icons/bs"; import React, { useEffect, useRef, useState } from 'react'; import { FaUndoAlt, FaRedoAlt, FaMoon, FaSun } from "react-icons/fa"; -import { TbCircleFilled, TbOvalFilled, TbSquareFilled, TbRectangleFilled } from "react-icons/tb"; import { useCanvas as useCanvasHook, useGlobalStore, useShortcuts } from "../../utils"; +import { TbCircleFilled, TbOvalFilled, TbSquareFilled, TbRectangleFilled } from "react-icons/tb"; enum ToolStage { @@ -26,8 +27,19 @@ export function Canvas() { return (
@@ -209,7 +220,8 @@ function useCanvas() { const { toolSettings, setActiveColor, setToolSettings, activeColor, activeColorPalette, setActiveColorPalette, colorStats, setColorStats, activeLayer, setActiveLayer, - activeFrame, canvasSize, setCanvasSize, onionSkin, frames + activeFrame, canvasSize, setCanvasSize, onionSkin, frames, + setActiveFrame, } = useGlobalStore(); const mainCanvasContainer = useRef(null); const mainCanvas = useCanvasHook(); @@ -218,7 +230,7 @@ function useCanvas() { const undoStack = useRef<{ frameID: any, layerID: any; image: any }[]>([]); const redoStack = useRef<{ frameID: any, layerID: any; image: any }[]>([]); - let [tilemode, setTilemode] = useState(false); + let tilemode = useRef(false); let mainCanvasZoom = useRef(15); let mouseState = useRef({ leftDown: false, @@ -260,16 +272,30 @@ function useCanvas() { canvas1.drawPixel(newX, newY, stateCache.current.toolSettings.size); }, "eraser": (x, y, toolButtonActive) => { - if (toolButtonActive) - toolState.current.stage = ToolStage.SAVE; - - // center the mouse cursor in the brush - let width = stateCache.current.toolSettings.size; - let height = stateCache.current.toolSettings.size; + let size = stateCache.current.toolSettings.eraseAll + ? 1 : stateCache.current.toolSettings.size; + let width = size; + let height = size; let newX = x == 0 ? 0 : x - Math.floor(width / 2); let newY = y == 0 ? 0 : y - Math.floor(height / 2); - canvas1.erasePixel(newX, newY, stateCache.current.toolSettings.size); + if (!toolButtonActive) { + canvas1.drawPixel(newX, newY, size, "rgba(255,0,0,1)"); + return; + } + + if (stateCache.current.toolSettings.eraseAll) { + canvas2.putImageData(activeLayer.image); + let points = canvas2.floodFill(x, y); + + points.forEach(point => { + canvas1.drawPixel(point.x, point.y, 1, "rgba(255,255,255,.004)"); + }); + } else { + canvas1.erasePixel(newX, newY, stateCache.current.toolSettings.size); + } + + toolState.current.stage = ToolStage.SAVE; }, "bucket": (x, y, toolButtonActive) => { if (!toolButtonActive) { @@ -671,6 +697,13 @@ function useCanvas() { })(); }, []); + // update frames and layers preview image + useEffect(() => { + setInterval(() => { + setActiveLayer(stateCache.current.activeLayer); + }, 500); + }, []); + // mouse event listeners useEffect(() => { const setMouseState = e => { @@ -731,11 +764,25 @@ function useCanvas() { setZoom(mainCanvasZoom.current); }, []); - // resize canvas + // resize canvas and layers useEffect(() => { mainCanvas.resize(canvasSize.width, canvasSize.height); canvas1.resize(canvasSize.width, canvasSize.height); canvas2.resize(canvasSize.width, canvasSize.height); + + const centerImageData = (oldImageData, newWidth, newHeight) => { + const startX = Math.max(0, Math.floor((newWidth - oldImageData.width) / 2)); + const startY = Math.max(0, Math.floor((newHeight - oldImageData.height) / 2)); + canvas1.resize(newWidth, newHeight); + canvas1.putImageData(oldImageData, startX, startY); + return canvas1.getImageData(); + }; + + frames.forEach(frame => { + frame.layers.forEach(layer => { + layer.image = centerImageData(layer.image, canvasSize.width, canvasSize.height); + }); + }); }, [canvasSize]); // update state cache @@ -746,13 +793,6 @@ function useCanvas() { // rebuild tooltip useEffect(() => { ReactTooltip.rebuild() }, [toolSettings]); - // tilemode - useEffect(() => { - let height = tilemode ? canvasSize.height * 3 : canvasSize.height; - let width = tilemode ? canvasSize.width * 3 : canvasSize.width; - mainCanvas.resize(width, height); - }, [tilemode]); - //! move to tools function setBrushSize(delta: number) { setToolSettings({ ...toolSettings, size: (toolSettings.size + delta < 1) ? 1 : (toolSettings.size + delta) }); @@ -781,8 +821,9 @@ function useCanvas() { } function resizeHandler(size: { height?: number, width?: number }) { - let newHeight = Math.max(1, size.height ?? canvasSize.height); - let newWidth = Math.max(1, size.width ?? canvasSize.width); + let newHeight = Number.isFinite(size.height) ? Math.max(1, size.height!) : canvasSize.height; + let newWidth = Number.isFinite(size.width) ? Math.max(1, size.width!) : canvasSize.width; + setCanvasSize({ height: newHeight, width: newWidth @@ -839,7 +880,7 @@ function useCanvas() { let { r, g, b, a } = stateCache.current.activeColor; canvas1.getCtx().fillStyle = `rgba(${r}, ${g}, ${b}, ${a / 255})`; - //* draw a preview of the selected tool + //* draw a preview of the selected tool into canvas1 toolHandlers[tool]?.(x, y, toolButtonActive); const canvas1Img = canvas1.getImageData(); @@ -869,7 +910,6 @@ function useCanvas() { stateCache.current.activeLayer.image!.data[i + 3] = 0; } } - setActiveLayer({ ...stateCache.current.activeLayer }); toolState.current.stage = ToolStage.PREVIEW; } @@ -906,6 +946,15 @@ function useCanvas() { mainCanvas.drawImage(canvas1.getElement(), 0, 0); // Copy the new pixels to the main canvas } }); + + if (tilemode.current) { + let width = stateCache.current.canvasSize.width * mainCanvasZoom.current; + let height = stateCache.current.canvasSize.height * mainCanvasZoom.current; + mainCanvasContainer.current!.style.background = `url(${mainCanvas.getElement().toDataURL()}) repeat center`; + mainCanvasContainer.current!.style.backgroundSize = `${width}px ${height}px`; + } else { + mainCanvasContainer.current!.style.background = `transparent`; + } } return { @@ -913,7 +962,6 @@ function useCanvas() { redo, setZoom, tilemode, - setTilemode, canvasSize, toolSettings, mainCanvasZoom, diff --git a/src/pages/Home/Layers.tsx b/src/pages/Home/Layers.tsx index 0c8ed4b..4a99db3 100644 --- a/src/pages/Home/Layers.tsx +++ b/src/pages/Home/Layers.tsx @@ -2,9 +2,9 @@ import { ILayer } from "../../types"; import { MdDelete } from "react-icons/md"; import { RiGitMergeFill } from "react-icons/ri"; import { IoEye, IoCopy } from "react-icons/io5"; -import React, { useEffect, useRef, useState } from 'react'; import { useCanvas, useGlobalStore } from "../../utils"; import { HiEyeOff, HiDocumentAdd } from "react-icons/hi"; +import React, { useEffect, useRef, useState } from 'react'; import { BsFillCaretDownFill, BsFillCaretUpFill } from "react-icons/bs"; @@ -13,7 +13,7 @@ export function Layers() { return (
-
+ - {/* mobile buttons */} - + -
- {data.projectList.filter(p => p !== data.projectName).reverse().map((project) => ( - props.loadProject(project)}> - {project} - - - - ))} -
- - -
+ {/* mobile buttons */} + + + + + + + {/* mobile buttons */} + ) } function useNav(props: IProps) { - const modal = useModal(); + const modalExport = useModal(); + const modalImport = useModal(); const canvas1 = useCanvas(); const canvas2 = useCanvas(); const canvas3 = useCanvas(); const { - projectName, setProjectName, frames, activeFrame, - activeLayer, canvasSize, colorPalettes + projectName, frames, activeFrame, + activeLayer, canvasSize, colorPalettes, + setProjectName, setFrames, setColorPalettes, + setCanvasSize, setActiveLayer, setActiveColorPalette } = useGlobalStore(); let [projectList, setProjectList] = useState([]); + // load local projects useEffect(() => { let cachedList = JSON.parse(localStorage.getItem("moth-projects") ?? "[]"); setProjectList(cachedList); }, []); - function getProject(): IProject { - return { - name: projectName, - frames: frames, - colorPalettes: colorPalettes, - canvas: canvasSize - }; - } + // save project locally + useEffect(() => { + let localProject = getProject(); + + let currentProjectList = JSON.parse(localStorage.getItem("moth-projects") ?? "[]"); + if (!currentProjectList.includes(localProject.name)) { + currentProjectList.push(localProject.name); + localStorage.setItem("moth-projects", JSON.stringify(currentProjectList)); + } + + if (currentProjectList.length > 10) { + let front = currentProjectList.shift(); + localStorage.removeItem(front); + localStorage.setItem("moth-projects", JSON.stringify(currentProjectList)); + } + + localStorage.setItem(localProject.name, JSON.stringify(localProject)); + }, [activeLayer]); function saveProjectName(name) { let currentProjectList = JSON.parse(localStorage.getItem("moth-projects") ?? "[]"); @@ -190,27 +191,49 @@ function useNav(props: IProps) { setProjectName(name); } - function deleteProject(project: string) { - if (!window.confirm("Are you sure you want to delete this project?")) return; + function getProject(): IProject { + return { + name: projectName, + frames: frames, + colorPalettes: colorPalettes, + canvas: canvasSize, + }; + } - let currentProjectList = JSON.parse(localStorage.getItem("moth-projects") ?? "[]"); - currentProjectList = currentProjectList.filter((p: string) => p !== project); - let projectListWithoutCurrent = currentProjectList.filter((p: string) => p !== projectName); + function loadProject(project) { + project.frames.forEach((frame) => { + frame.symbol = Symbol(); + frame.layers.forEach((layer) => { + layer.image = new ImageData(new Uint8ClampedArray(Object.values(layer.image.data)), project.canvas.width, project.canvas.height); + layer.symbol = Symbol(); + }); + }); + project.colorPalettes.forEach((palette) => { + palette.symbol = Symbol(); + }) - localStorage.setItem("moth-projects", JSON.stringify(currentProjectList)); - localStorage.removeItem(project); - setProjectList(projectListWithoutCurrent); + setProjectName(project.name); + setCanvasSize(project.canvas); + setFrames(project.frames); + setColorPalettes(project.colorPalettes); + setActiveColorPalette(project.colorPalettes[0]) + setActiveLayer(project.frames[0].layers[0]); + } + + function loadProjectFromLocalStorage(projectName) { + let project = JSON.parse(localStorage.getItem(projectName) ?? "{}"); + loadProject(project); } async function importProject() { let [fileHandle] = await (window as any).showOpenFilePicker({ - types: [{ accept: { 'image/*': ['.png'] } }], + types: [{ accept: { 'image/*': [".png"] } }], }); let file = await fileHandle.getFile(); let fileReader = new FileReader(); fileReader.readAsArrayBuffer(file); - fileReader.onload = () => { + fileReader.onload = async () => { let data = new Uint8Array(fileReader.result as ArrayBuffer); let chunks = pngExtract(data); @@ -222,37 +245,72 @@ function useNav(props: IProps) { return chunk.keyword === "moth"; })[0]; - if (mothChunk) { - //! do something with this - JSON.parse(Base64.decode(mothChunk.text)); + if (mothChunk) { // load project + let project = JSON.parse(Base64.decode(mothChunk.text)); + loadProject(project); } } } + async function importImage() { + let [fileHandle] = await (window as any).showOpenFilePicker({ + types: [{ accept: { 'image/*': [".png", ".jpg", ".jpeg"] } }], + }); + + let file = await fileHandle.getFile(); + let fileReader = new FileReader(); + fileReader.readAsArrayBuffer(file); + fileReader.onload = async () => { + let data = new Uint8Array(fileReader.result as ArrayBuffer); + let blob = new Blob([data], { type: file.type }); + let url = URL.createObjectURL(blob); + let img = new Image(); + img.src = url; + + await img.decode(); + let canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + + let ctx = canvas.getContext('2d'); + ctx!.drawImage(img, 0, 0); + + let imageData = ctx!.getImageData(0, 0, canvas.width, canvas.height); + let layer = { + name: "Layer 1", + image: imageData, + symbol: Symbol(), + opacity: 255, + }; + setActiveLayer(layer); + setCanvasSize({ width: img.width, height: img.height }); + setProjectName(file.name); + } + } + function exportProject(settings?: IExportSettings) { let height = canvasSize.height; let width = (settings?.frameOnly || settings?.layerOnly) ? canvasSize.width : canvasSize.width * frames.length; canvas1.resize(width, height); + canvas2.resize(canvasSize.width, canvasSize.height); + canvas3.resize(canvasSize.width, canvasSize.height); let newFrames = (settings?.frameOnly || settings?.layerOnly) ? [activeFrame] : frames; newFrames.forEach((frame, i) => { - canvas2.resize(canvasSize.width, canvasSize.height); - let layersRevered = frame.layers.slice().reverse(); let layers = (settings?.layerOnly) ? [activeLayer] : layersRevered; layers.forEach(layer => { - canvas3.resize(canvasSize.width, canvasSize.height); - canvas3.putImageData(layer.image); canvas2.drawImage(canvas3.getElement()); }); - canvas1.drawImage(canvas2.getElement(), (i * canvasSize.width), 0); + canvas1.drawImage(canvas2.getElement(), (width / newFrames.length) * i, 0, canvasSize.width, canvasSize.height); + canvas2.clear(); }); // get current project as png @@ -278,6 +336,18 @@ function useNav(props: IProps) { anchor.click(); } + function deleteProject(project: string) { + if (!window.confirm("Are you sure you want to delete this project?")) return; + + let currentProjectList = JSON.parse(localStorage.getItem("moth-projects") ?? "[]"); + currentProjectList = currentProjectList.filter((p: string) => p !== project); + let projectListWithoutCurrent = currentProjectList.filter((p: string) => p !== projectName); + + localStorage.setItem("moth-projects", JSON.stringify(currentProjectList)); + localStorage.removeItem(project); + setProjectList(projectListWithoutCurrent); + } + function createGif() { var gif = new GIF({ workerScript: '/moth/js/gif.worker.js', @@ -306,13 +376,16 @@ function useNav(props: IProps) { } return { - modal, + modalExport, + modalImport, projectName, projectList, createGif, deleteProject, exportProject, importProject, + importImage, saveProjectName, + loadProjectFromLocalStorage, } } diff --git a/src/pages/Home/Tools.tsx b/src/pages/Home/Tools.tsx index 25d9426..e9ee27f 100644 --- a/src/pages/Home/Tools.tsx +++ b/src/pages/Home/Tools.tsx @@ -2,9 +2,9 @@ import { ITool } from '../../types'; import ReactTooltip from 'react-tooltip'; import { IoMdMove } from "react-icons/io"; import { AiFillFire } from "react-icons/ai"; -import { BsBucketFill } from "react-icons/bs"; +import { BsBucketFill, BsEraserFill } from "react-icons/bs"; import { GiMirrorMirror } from "react-icons/gi"; -import { MdInvertColors } from "react-icons/md"; +import { MdInvertColors, MdOutlineCallReceived } from "react-icons/md"; import { IoBandageSharp } from "react-icons/io5"; import React, { useEffect, useState } from 'react'; import { IoColorWandSharp, IoNuclear } from "react-icons/io5"; @@ -41,7 +41,7 @@ export function Tools() { data-for="tooltip" className={`mb-2 c-button --fourth --sm ${data.getButtonStyles("eraser")}`} onMouseDown={(e) => data.updateTool(e, "eraser")}> - + diff --git a/src/pages/Home/index.tsx b/src/pages/Home/index.tsx index edece54..379b689 100644 --- a/src/pages/Home/index.tsx +++ b/src/pages/Home/index.tsx @@ -4,7 +4,6 @@ import { Canvas } from './Canvas'; import { Colors } from './Colors'; import { Frames } from './Frames'; import { Layers } from './Layers'; -import { IProject } from "../../types"; import ReactTooltip from 'react-tooltip'; import { ImCross } from "react-icons/im"; import { IoLayers } from "react-icons/io5"; @@ -20,8 +19,7 @@ export function Home() {
-