-
Clone the Project
Clone the repository to your local machine:git clone [your-repo-url] cd [your-repo-directory] -
Install Dependencies
Run the following command to install all required dependencies:npm install
-
Run the Plugin
Use the following commands to start or build the plugin:- To start development mode with hot reloading: 🔄
npm run watch
- To serve the plugin with a local development server: 💻
npm start
- To create a production build: 🏗️
npm run build
Here are the scripts defined in
package.json:"scripts": { "build": "webpack", "watch": "webpack --watch", "start": "webpack serve" }
- To start development mode with hot reloading: 🔄
-
Open Figma
Open Figma and navigate to your working page. -
Add the Plugin
Right-click on the page and go to Plugins > Manage Plugins. -
Add this Project Manifest
In the Manage Plugins section, add the plugin by selecting this project'smanifest.json. -
Watch Mode
If running in watch mode, changes to the sandbox code z(code.ts) will automatically update. However, changes to iframe (UI) code (ui.tsx) will require reloading the plugin.
The project is organized as follows:
- assets/
Contains application assets including constants and configuration files.- constants/
commands.tsenv.tserrorMessages.tsindex.tsmessages.tsroutes.tstypes.tsurl.ts
- constants/
- controllers/
Contains the business logic and controllers for commands.AccountController.tsChangeApiKeyController.tsIntroController.tsRemoveBackgroundController.tsSupportController.tsEnhanceController.tsindex.ts
- dist/
Contains bundled and built files for the plugin. - node_modules/
Dependencies installed vianpm install. - public/
Static assets for the plugin, e.g., HTML templates. - routes/
Routing logic for commands.CommandRouter.ts
- services/
Service files for specific utilities or APIs.ImageProcessor.ts
- src/
Contains the source code for the plugin's frontend (UI).- components/
Contains React components for the UI.Account/ChangeAPIkey/IntroPage/LoadingSpinner/RemoveBackground/Selector/Support/Upscale/
- styles/
Contains global and component-specific SCSS files._colors.scss_variables.scssglobal.scss
- utils/
Utility functions and helpers.imageProcessor.tscode.ts
ui.tsx
Main UI logic for the plugin.
- components/
- manifest.json
Configuration file for the Figma plugin. - package.json
Project metadata and dependencies. - package-lock.json
Dependency tree lock file. - README.md
Documentation for the project. - tsconfig.json
TypeScript configuration file.
Commands are registered in manifest.json and linked in routes.ts. Follow these steps to add a new command:
- Register your command in
manifest.json. - Add the controller in the
controllers/directory. - Update the
index.tsin thecontrollers/folder to include the new controller. - Add the route in
routes.ts. - Create a UI component for the command in
src/components/.
Example of routes.ts:
import commands from "./commands";
import controllersIndex from "@controllers/index";
const ROUTES = {
[commands.COMMAND_REMOVEBACKGROUND]: controllersIndex.RemoveBackgroundController,
[commands.COMMAND_UPSCALE]: controllersIndex.EnhanceController,
[commands.COMMAND_ACCOUNT]: controllersIndex.AccountController,
[commands.COMMAND_SUPPORT]: controllersIndex.SupportController,
};
export default ROUTES;Below is an example of the UI logic:
import React, { useEffect, useState } from 'react';
import { createRoot } from 'react-dom/client';
import { IntroPage, Account, ChangeAPIkey, Support, RemoveBackground, Upscale, LoadingSpinner } from '@components/index';
import { NO_INTERNET_ERR, TYPE_COMMAND, TYPE_IMAGEBYTES, TYPE_KEY, COMMAND_REMOVEBACKGROUND, COMMAND_ACCOUNT, COMMAND_SUPPORT, COMMAND_UPSCALE, COMMAND_INTRO, COMMAND_CHANGE_API_KEY } from "@constants/index";
import '@styles/global.scss';
const App = () => {
if (navigator.onLine === false) {
parent.postMessage({ pluginMessage: NO_INTERNET_ERR }, "*");
}
const [page, setPage] = useState<JSX.Element | null>(null);
const [apiKey, setApiKey] = useState<string>('');
const [command, setCommand] = useState<string>('');
const [imageBytes, setImageBytes] = useState<Uint8Array>(new Uint8Array());
const setPageLogic = () => {
switch (command) {
case COMMAND_REMOVEBACKGROUND:
setPage(<RemoveBackground imageBytes={imageBytes} gottenKey={apiKey} />);
break;
case COMMAND_UPSCALE:
setPage(<Upscale imageBytes={imageBytes} gottenKey={apiKey} />);
break;
case COMMAND_ACCOUNT:
setPage(<Account gottenKey={apiKey} />);
break;
case COMMAND_SUPPORT:
setPage(<Support />);
break;
case COMMAND_INTRO:
setPage(<IntroPage />);
break;
case COMMAND_CHANGE_API_KEY:
setPage(<ChangeAPIkey handleClose={() => {}} />);
break;
default:
setPage(null);
}
};
useEffect(() => {
const messageHandler = (event: MessageEvent) => {
const { pluginMessage } = event.data;
if (pluginMessage) {
if (pluginMessage.type === TYPE_KEY) setApiKey(pluginMessage.api_key);
if (pluginMessage.type === TYPE_IMAGEBYTES) setImageBytes(pluginMessage.buffer);
if (pluginMessage.type === TYPE_COMMAND) setCommand(pluginMessage.command);
}
};
window.addEventListener('message', messageHandler);
return () => {
window.removeEventListener('message', messageHandler);
};
}, []);
useEffect(() => {
setPageLogic();
}, [command, apiKey]);
return (
<div className="root">
{page ? page : <LoadingSpinner />}
</div>
);
};
const rootElement = document.getElementById('root');
if (rootElement) {
const root = createRoot(rootElement);
root.render(<App />);
}