create & deploy a plugin doc

This commit is contained in:
María Valderrama 2024-06-05 14:11:47 +02:00
parent 8b99539e07
commit df2db4d207
14 changed files with 510 additions and 135 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
img/plugins/cf_new_page.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
img/plugins/drag&drop.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
img/plugins/vue_dist.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

@ -1,84 +0,0 @@
---
title: 5.5. Creating a Plugin outside Nx monorepo
---
# Creating a Plugin outside Nx monorepo
This guide covers the configuration of a plugin with your chosen framework outside the penpot monorepo environment.
For the starter vanilla template go to [Penpot Plugin Starter Template](https://github.com/penpot/penpot-plugin-starter-template).
For other creation guides:
- [Create a plugin](link-to-create-plugin.md)
- [Create an angular plugin](link-to-create-angular-plugin.md)
Keep in mind that this guide is written in general terms and different frameworks may need a few tweaks.
### Step 1: Create your app
Create your own app with the framework of your choice.
### Step 2: Install penpot libraries
Follow the documentation in [Publishing packages](link-to-publish-package.md) to locally publish and then install `@penpot/plugin-types` and `@penpot/plugin-styles` in your project.
Make sure to add the types to your configuration and import the styles in your css.
### Step 3: Create the plugin file
The plugin file can be either `.js` or `.ts`. In the examples we will be using `plugin.ts`. You can place it wherever you like, but it normally goes alongside the main files inside the `src/` folder.
You can start with something like this:
```ts
penpot.ui.open("Plugin name", "", {
width: 500,
height: 600,
});
```
Check the [Api Documentation](/technical-guide/plugins/api) for more.
### Step 4: Build the plugin file
In order to make your `plugin.ts` reachable, you need to parse it and make it public. You can achieve this through multiple solutions, but a quick way to do so is using the `esbuild` package.
```bash
$ npm i -D esbuild # install as dev dependency
```
```bash
$ esbuild your-folder/plugin.ts --minify --outfile=your-folder/assets/plugin.js
```
You can add it to your `package.json` scripts like this:
```json
"scripts": {
"start": "npm run build:plugin && your-serve-command",
"build:plugin": "esbuild your-folder/plugin.ts --minify --outfile=your-folder/assets/plugin.js"
[...]
},
```
### Step 5: Configure the manifest file
Now that everything is in place, you need a `manifest.json` file. The location may vary between frameworks, but it's usually located somewhere reachable like `assets/` or `public/`.
```json
{
"name": "Plugin name",
"description": "Plugin description",
"host": "http://localhost:3000",
"code": "/plugin.js",
"icon": "your-folder/assets/icon.png",
"permissions": ["page:read", "file:read", "selection:read"]
}
```
### Test the plugin
To test the plugin locally you need to serve it. Remember to use the same port that you configured in the `manifest.json`. Make sure that both `manifest.json` and `plugin.js` are reachable.
To install the plugin in penpot use the `manifest.json` url. Example: `http://localhost:XXXX/assets/manifest.json`

View File

@ -0,0 +1,356 @@
---
title: 5.2. Create a Plugin
---
# Create a Plugin
This guide covers the creation of a Penpot plugin with the most popular front frameworks.
You can check the examples in [Plugin examples](https://github.com/penpot/plugin-examples).
**Related:**
- For the starter vanilla template go to [Penpot Plugin Starter Template](https://github.com/penpot/penpot-plugin-starter-template).
- For the nx monorepo documentation go to [Creating a Plugin](https://github.com/penpot/penpot-plugins/blob/main/docs/create-plugin.md) or [Creating a Plugin (angular)](https://github.com/penpot/penpot-plugins/blob/main/docs/create-angular-plugin.md).
## Step 1. Create a project
Create your own app with the framework of your choice.
| Framework | Command | Version\* |
| --------- | ----------------------------------------------------------- | --------- |
| Angular | `ng new plugin-name` | 18.0.0 |
| React | `npm create vite@latest plugin-name -- --template react-ts` | 18.2.0 |
| Vue | `npm create vue@latest` | 3.4.21 |
_\*: version we used in the examples._
## Step 2. Install Penpot libraries
There are two libraries that can help you with your plugin's development. They are `@penpot/plugin-styles` and `@penpot/plugin-types`.
### Plugin styles
`@penpot/plugin-style` contains styles to help build the UI for Penpot plugins. To check the styles go to [Plugin styles](https://penpot.github.io/penpot-plugins/).
```bash
npm install @penpot/plugin-styles
```
You can add the styles to your global css file.
```css
@import "@penpot/plugin-styles/styles.css";
```
### Plugin types
`@penpot/plugin-types` contains the typings for the Penpot Plugin API.
```bash
npm install @penpot/plugin-types
```
If you're using typescript, don't forget to add `@penpot/plugin-types` to your typings in your `tsconfig.json`.
```json
{
"compilerOptions": {
[...]
"typeRoots": ["./node_modules/@types", "./node_modules/@penpot"],
"types": ["plugin-types"],
}
}
```
## Step 3. Create a plugin file
A plugin file is needed to interact with Penpot and its API. You can use either javascript or typescript and it can be placed wherever you like. It normally goes alongside the main files inside the `src/` folder.
You can start with something like this:
```ts
penpot.ui.open("Plugin name", "", {
width: 500,
height: 600,
});
```
The sizing values are optional. By default the plugin will open with a size of 285x540 pixels.
Check the [Api Documentation](/technical-guide/plugins/api) for more.
## Step 4. Build the plugin file
This step is for locally serving purposes, for a detailed guide about building and deploying you can check the documentation at [Deployment](/technical-guide/plugins/deployment).
Note: if you're using javascript you can skip this step by placing the `plugin.js` file directly in your `public\` folder, like we did in the React example.
If you wish to run your plugin locally and test it live you need to make your plugin file reachable. Right now, your `plugin.ts` file is somewhere in the `src\` folder, and you can't access it through `http://localhost:XXXX/plugin.js`.
You can achieve this through multiple solutions but we offer two simple ways of doing so. Of course you can come up with your own.
#### Esbuild
```bash
$ npm i -D esbuild # install as dev dependency
```
Now you can use esbuild to parse and move your plugin file.
```bash
esbuild your-folder/plugin.ts --minify --outfile=your-folder/public/plugin.js
```
You can add it to your `package.json` scripts so you don't need to manually re-run the build:
```json
"scripts": {
"start": "npm run build:plugin && ng serve",
"build:plugin": "esbuild your-folder/plugin.ts --minify --outfile=your-folder/public/plugin.js"
[...]
},
```
Keep in mind that you'll need to build again your plugin file if you modify it mid-serve.
#### Vite
If you're using Vite you can simply edit the configuration file and add the build to your `vite.config.ts`.
```ts
export default defineConfig({
[...]
build: {
rollupOptions: {
input: {
plugin: "src/plugin.ts",
index: "./index.html",
},
output: {
entryFileNames: "[name].js",
},
},
},
preview: {
port: XXXX,
},
});
```
And then add the following scripts to your `package.json`:
```json
"scripts": {
"dev": "vite build --watch & vite preview",
"build": "tsc && vite build",
[...]
}
```
## Step 5. Configure the manifest file
Now that everything is in place you need a `manifest.json` file to provide Penpot with your plugin data. Remember to make it reachable by placing it in the `public/` folder.
```json
{
"name": "Plugin name",
"description": "Plugin description",
"code": "/plugin.js",
"icon": "/icon.png",
"permissions": ["page:read", "file:read", "selection:read"]
}
```
## Live serve
To test the plugin locally you need to serve it.
Make sure that both `http://localhost:XXXX/manifest.json` and `http://localhost:XXXX/plugin.js` can be reached.
Use your `manifest.json` url to install your plugin in Penpot and test it live.
## Penpot theme (optional)
Penpot have a dark and a light theme. This step covers a way to get this value in your plugin so you can customize it to match the current Penpot theme.
### Common
The common steps includes modifying the plugin file to add the theme. We also added a basic interface file for typescript. This file is supposed to grow as you create more Events for your plugin.
#### Typescript
- Add a `model.ts` along the `plugin.ts` file with the following:
```ts
/**
* This file contains the typescript interfaces for the plugin events.
*/
export interface ThemePluginEvent {
type: "theme";
content: string;
}
export type PluginMessageEvent = ThemePluginEvent;
```
- `plugin.ts`
```ts
import type { PluginMessageEvent } from "./model";
penpot.ui.open("Plugin name", `?theme=${penpot.getTheme()}`, {
width: 500,
height: 600,
});
penpot.on("themechange", (theme) => {
sendMessage({ type: "theme", content: theme });
});
function sendMessage(message: PluginMessageEvent) {
penpot.ui.sendMessage(message);
}
```
#### Javascript
- `plugin.js`
```js
penpot.ui.open("Plugin name", `?theme=${penpot.getTheme()}`, {
width: 500,
height: 600,
});
penpot.on("themechange", (theme) => {
sendMessage({ type: "theme", content: theme });
});
function sendMessage(message) {
penpot.ui.sendMessage(message);
}
```
### Angular
- `app.component.ts`
```ts
import { Component, inject } from "@angular/core";
import { toSignal } from "@angular/core/rxjs-interop";
import { ActivatedRoute, RouterOutlet } from "@angular/router";
import { fromEvent, map, filter, take, merge } from "rxjs";
import { PluginMessageEvent } from "../model";
@Component({
selector: "app-root",
standalone: true,
imports: [RouterOutlet],
templateUrl: "./app.component.html",
styleUrl: "./app.component.css",
host: {
"[attr.data-theme]": "theme()",
},
})
export class AppComponent {
public route = inject(ActivatedRoute);
public messages$ = fromEvent<MessageEvent<PluginMessageEvent>>(
window,
"message"
);
public initialTheme$ = this.route.queryParamMap.pipe(
map((params) => params.get("theme")),
filter((theme) => !!theme),
take(1)
);
public theme = toSignal(
merge(
this.initialTheme$,
this.messages$.pipe(
filter((event) => event.data.type === "theme"),
map((event) => {
return event.data.content;
})
)
)
);
}
```
### React
- `App.js`
```js
import "./App.css";
import { useState } from "react";
function App() {
const url = new URL(window.location.href);
const initialTheme = url.searchParams.get("theme");
const [theme, setTheme] = useState(initialTheme || null);
window.addEventListener("message", (event) => {
if (event.data.type === "theme") {
setTheme(event.data.content);
}
});
return <div data-theme={theme}>Welcome to your plugin!</div>;
}
export default App;
```
### Vue
- `App.vue`
```html
<script setup lang="ts">
import { onMounted } from "vue";
import { signal } from "vue-signals";
const theme = signal<string | null>(null);
onMounted(() => {
const url = new URL(window.location.href);
const initialTheme = url.searchParams.get("theme");
if (initialTheme) {
theme.set(initialTheme as string);
}
window.addEventListener("message", (event) => {
if (event.data.type === "theme") {
theme.set(event.data.content);
}
});
});
</script>
<template>
<div :data-theme="theme()">Welcome to your plugin!</div>
</template>
```
### Styling
It may vary depending on where you have placed the theme attribute.
```css
[data-theme="dark"] {
/** Dark styles */
}
[data-theme="light"] {
/** Light styles */
}
```

View File

@ -4,71 +4,183 @@ title: 5.3. Deployment
# Deployment
## Prepare
When it comes to deploying your plugin there are several platforms to choose from. Each platform has its unique features and benefits, so the choice depends on you.
Before we deploy our plugin we need to have a domain at hand. You can use your own domain name and configure it through your chosen hosting service, but in this example, we are going to use Netlify and a free netlify.app domain.
In this guide you will found some options for static sites that have free plans.
We will be using `https://example-plugin-penpot.netlify.app`. Make sure to check your domain availability beforehand or else you may need to repeat all the steps.
## Building your project
By default the plugin is ready to run locally at `http://localhost:4201`, and we need it to run remotely at `https://example-plugin-penpot.netlify.app`. **You may or may not be using a diferent host or port, so keep that in mind**.
The building may vary between frameworks but if you had previously configured your scripts in `package.json`, `npm run build` should work.
Also note that if you want to keep working locally you will need to revert all these changes. You could use a different branch, copy the project folder or simply checkout the changes once you have built your plugin.
The resulting build should be located somewhere in the `dist/` folder, maybe somewhere else if you have configured so.
Now that you know everything we can proceed with the changes.
Be wary that some framework's builders can add additional folders like `apps/project-name/`, `project-name/` or `browser/`.
### Manifest
Examples:
First of all, we need to update the manifest file so it points to the correct plugin configuration. Go to `public/manifest.json` and change the line where it states that the code is in `http://localhost:4201`. Use the url where the plugin will be hosted, like this:
![Vue dist example](/img/plugins/vue_dist.png)
![Angular dist example](/img/plugins/angular_dist.png)
```json
{
"name": "Example plugin",
"code": "https://example-plugin-penpot.netlify.app/plugin.js",
"permissions": ["page:read", "file:read", "selection:read"]
}
```
## Netlify
### Configuration
### CORS issues
Our manifest is ready, but the opening function must be also updated. Go to `src/plugin.ts` (or the file where you have placed your `penpot.ui.open()`) and, again, change the `http://localhost:4201` to your chosen domain.
```ts
penpot.ui.open("Plugin name", "https://example-plugin-penpot.netlify.app", {
width: 500,
height: 600,
});
```
### Headers
Last but not least, to avoid cross-origin issues you can add a `_header` file to the `public/` folder. It should contain something similar to:
To avoid these issues you can add a `_headers` file to your plugin. Place it in the `public/` folder or alongside the main files.
```js
/*
Access-Control-Allow-Origin: *
```
Feel free to configure it as you need.
### Drag and drop
## Build
Netlify offers a simple drag and drop method. Check [Netlify Drop](https://app.netlify.com/drop).
Now that our plugin is ready for deployment we need to build it.
#### How to deploy
For building the plugin we simply need to run the command: `npx nx build example-plugin --emptyOutDir=true`.
1. Build your project
The resulting code would be found in `dist/apps/example-plugin` by default.
```bash
npm run build
```
## Deploy
2. Go to [Netlify Drop](https://app.netlify.com/drop).
Deployment may vary according to the chosen host service. To deploy your plugin to Netlify you simply need to drag and drop the folder found in `dist/apps/example-plugin`into the Netlify Sites.
3. Drag and drop the build folder into Netlify Sites. Dropping the whole dist may not work, you should drop the folder where the main files are located.
Once the deployment is complete, don't forget to configure the site name through Site Configuration in Netlify, so the resulting url match with the one we provided in the manifest and the plugin opening function. In this example, the name should be `example-plugin-penpot`, which would result in the url `https://example-plugin-penpot.netlify.app`.
![Angular dist example](/img/plugins/drag&drop.gif)
## Test
4. Done!
Congratulations, you have successfully deployed your first plugin! Now it's time to test it.
### Connect to Git
You need to:
Netlify allows you to import an existing project from GitHub, GitLab, Bitbucket or Azure DevOps.
- Open some penpot project as usual.
- Load the plugin with `ɵloadPlugin({ manifest: 'https://example-plugin-penpot.netlify.app/manifest.json' })`.
- [Configure builds](https://docs.netlify.com/configure-builds/overview/).
#### How to deploy
1. Go to [Start](https://app.netlify.com/start) and connect with your repository. Allow Netlify to be installed in either all your projects or just the selected ones.
![Netlify git installation](/img/plugins/install_netlify.png)
2. Configure your build settings. Netlify auto-detects your framework and offers a basic configuration. This is usually enough.
![Netlify git configuration](/img/plugins/build_settings.png)
3. Deploy your plugin.
## Cloudflare
### CORS issues
To avoid these issues you can add a `_headers` file to your plugin. Place it in the `public/` folder or alongside the main files.
```js
/*
Access-Control-Allow-Origin: *
```
### Direct upload
You can directly upload your plugin folder.
- [Direct upload](https://developers.cloudflare.com/pages/get-started/direct-upload/)
#### How to deploy
1. Build your plugin.
```bash
npm run build
```
2. Go to Workers & Pages > Create > Page > Upload assets.
3. Create a new page.
![Cloudflare new page](/img/plugins/cf_new_page.png)
4. Upload your plugin files. You can drag and drop or select the folder.
![Cloudflare page upload files](/img/plugins/cf_upload_files.png)
5. Deploy site.
### Connect to Git
Cloudflare allows you to import an existing project from GitHub or GitLab.
- [Git integration](https://developers.cloudflare.com/pages/get-started/git-integration/)
#### How to deploy
1. Go to Workers & Pages > Create > Page > Connect to git
2. Select a repository. Allow Cloudflare to be installed in either all your projects or just the selected ones.
![Cloudflare git installation](/img/plugins/install_cloudflare.png)
4. Configure your build settings.
![Cloudflare git configuration](/img/plugins/cf_build_settings.png)
5. Save and deploy.
## Surge
Surge provides a CLI tool for easy deployment.
- [Getting Started](https://surge.sh/help/getting-started-with-surge).
### CORS issues
To avoid these issues you can add a `CORS` file to your plugin. Place it in the `public/` folder or alongside the main files.
The `CORS` can contain a `*` for any domain, or a list of specific domains.
Check [Enabling Cross-Origin Resources sharing](https://surge.sh/help/enabling-cross-origin-resource-sharing).
### How to deploy
1. Install surge CLI globally and log into your account or create one.
```bash
npm install --global surge
surge login
# or
surge signup
```
2. Create a CORS file to allow all sites.
```bash
echo '*' > public/CORS
```
3. Build your project.
```bash
npm run build
```
4. Start surge deployment
```bash
surge
# Your plugin build folder
project: /home/user/example-plugin/dist/
# your domain, surge offers a free .surge.sh domain and free ssl
domain: https://example-plugin-penpot.surge.sh
upload: [====================] 100% eta: 0.0s (10 files, 305761 bytes)
CDN: [====================] 100%
encryption: *.surge.sh, surge.sh (346 days)
IP: XXX.XXX.XXX.XXX
Success! - Published to example-plugin-penpot.surge.sh
```
5. Done!

View File

@ -1,7 +0,0 @@
---
title: 5.2. Your first plugin
---
# First plugin
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi.

View File

@ -9,8 +9,6 @@ In this section you will learn the basics about penpot plugins and how to use th
Whether you want to install an existing plugin or learn how to create your own, this guide got you covered.
[1]: /technical-guide/plugins/getting-started/
[2]: /technical-guide/plugins/first-plugin/
[2]: /technical-guide/plugins/create-a-plugin/
[3]: /technical-guide/plugins/deployment/
[4]: /technical-guide/plugins/api/
[5]: /technical-guide/plugins/create-a-plugin-outside-nx/
[6]: /technical-guide/plugins/name/