mirror of
https://github.com/penpot/penpot-plugins.git
synced 2025-01-21 06:02:34 -05:00
feat: add poc-state-plugin app
This commit is contained in:
parent
2300ce23ac
commit
e618cb1e9b
20 changed files with 9799 additions and 236 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -40,3 +40,7 @@ Thumbs.db
|
|||
|
||||
.nx/cache
|
||||
.env
|
||||
|
||||
.angular
|
||||
|
||||
**/assets/plugin.js
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# Add files here to ignore them from prettier formatting
|
||||
/dist
|
||||
/coverage
|
||||
/.nx/cache
|
||||
/.nx/cache
|
||||
.angular
|
||||
|
|
36
apps/poc-state-plugin/.eslintrc.json
Normal file
36
apps/poc-state-plugin/.eslintrc.json
Normal file
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"extends": ["../../.eslintrc.base.json"],
|
||||
"ignorePatterns": ["!**/*", "**/assets/*.js"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts"],
|
||||
"extends": [
|
||||
"plugin:@nx/angular",
|
||||
"plugin:@angular-eslint/template/process-inline-templates"
|
||||
],
|
||||
"rules": {
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "attribute",
|
||||
"prefix": "app",
|
||||
"style": "camelCase"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "element",
|
||||
"prefix": "app",
|
||||
"style": "kebab-case"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["*.html"],
|
||||
"extends": ["plugin:@nx/angular-template"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
94
apps/poc-state-plugin/project.json
Normal file
94
apps/poc-state-plugin/project.json
Normal file
|
@ -0,0 +1,94 @@
|
|||
{
|
||||
"name": "poc-state-plugin",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "application",
|
||||
"prefix": "app",
|
||||
"sourceRoot": "apps/poc-state-plugin/src",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"buildPlugin": {
|
||||
"executor": "@nx/esbuild:esbuild",
|
||||
"outputs": [
|
||||
"{options.outputPath}"
|
||||
],
|
||||
"options": {
|
||||
"minify": true,
|
||||
"outputPath": "apps/poc-state-plugin/src/assets/",
|
||||
"main": "apps/poc-state-plugin/src/plugin.ts",
|
||||
"tsConfig": "apps/poc-state-plugin/tsconfig.plugin.json",
|
||||
"generatePackageJson": false,
|
||||
"format": [
|
||||
"esm"
|
||||
],
|
||||
"deleteOutputPath": false
|
||||
}
|
||||
},
|
||||
"build": {
|
||||
"executor": "@angular-devkit/build-angular:application",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"outputPath": "dist/apps/poc-state-plugin",
|
||||
"index": "apps/poc-state-plugin/src/index.html",
|
||||
"browser": "apps/poc-state-plugin/src/main.ts",
|
||||
"polyfills": ["zone.js"],
|
||||
"tsConfig": "apps/poc-state-plugin/tsconfig.app.json",
|
||||
"assets": [
|
||||
"apps/poc-state-plugin/src/favicon.ico",
|
||||
"apps/poc-state-plugin/src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"libs/plugins-styles/src/lib/styles.css",
|
||||
"apps/poc-state-plugin/src/styles.css"],
|
||||
"scripts": [],
|
||||
"optimization": {
|
||||
"scripts": true,
|
||||
"styles": true,
|
||||
"fonts": false
|
||||
}
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kb",
|
||||
"maximumError": "1mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"optimization": false,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production",
|
||||
"dependsOn": ["buildPlugin"]
|
||||
},
|
||||
"serve": {
|
||||
"executor": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"buildTarget": "poc-state-plugin:build:production"
|
||||
},
|
||||
"development": {
|
||||
"buildTarget": "poc-state-plugin:build:development",
|
||||
"port": 4202
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"executor": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"buildTarget": "poc-state-plugin:build"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
87
apps/poc-state-plugin/src/app/app.component.css
Normal file
87
apps/poc-state-plugin/src/app/app.component.css
Normal file
|
@ -0,0 +1,87 @@
|
|||
html {
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
||||
'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif,
|
||||
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
line-height: 1.5;
|
||||
tab-size: 4;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
body {
|
||||
font-family: inherit;
|
||||
line-height: inherit;
|
||||
margin: 0;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
p,
|
||||
pre {
|
||||
margin: 0;
|
||||
}
|
||||
*,
|
||||
::before,
|
||||
::after {
|
||||
box-sizing: border-box;
|
||||
border-width: 0;
|
||||
border-style: solid;
|
||||
border-color: currentColor;
|
||||
}
|
||||
h1,
|
||||
h2 {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
pre {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||
'Liberation Mono', 'Courier New', monospace;
|
||||
}
|
||||
svg {
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
svg {
|
||||
shape-rendering: auto;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-block-end: var(--spacing-12);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
margin-block-end: var(--spacing-12);
|
||||
}
|
||||
|
||||
.help {
|
||||
color: #6b7280;
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
padding-inline-start: var(--spacing-12);
|
||||
}
|
||||
|
||||
.name-wrap {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.name-wrap input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.actions-wrap {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
140
apps/poc-state-plugin/src/app/app.component.ts
Normal file
140
apps/poc-state-plugin/src/app/app.component.ts
Normal file
|
@ -0,0 +1,140 @@
|
|||
import { Component, signal } from '@angular/core';
|
||||
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import type { PenpotShape } from '@penpot/plugin-types';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'app-root',
|
||||
imports: [ReactiveFormsModule],
|
||||
template: `
|
||||
<div class="wrapper" [attr.data-theme]="theme()">
|
||||
<h1>Test area!</h1>
|
||||
|
||||
<p>
|
||||
Current project name: <span>{{ projectName() }}</span>
|
||||
</p>
|
||||
|
||||
<form [formGroup]="form" (ngSubmit)="updateName()">
|
||||
<div class="name-wrap">
|
||||
<label>Selected Shape: </label>
|
||||
<input type="text" formControlName="name" />
|
||||
<button type="submit">Update</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="actions-wrap">
|
||||
<button type="button" (click)="createRect()">+Rect</button>
|
||||
<button type="button" (click)="moveX()">Move X</button>
|
||||
<button type="button" (click)="moveY()">Move Y</button>
|
||||
<button type="button" (click)="resizeW()">Resize W</button>
|
||||
<button type="button" (click)="resizeH()">Resize H</button>
|
||||
<button type="button" (click)="loremIpsum()">Lorem Ipsum</button>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<button
|
||||
(click)="close()"
|
||||
type="button"
|
||||
data-appearance="primary"
|
||||
data-variant="destructive"
|
||||
class="act-close-plugin"
|
||||
>
|
||||
Close plugin
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
styleUrl: './app.component.css',
|
||||
})
|
||||
export class AppComponent {
|
||||
#pageId: null | string = null;
|
||||
#fileId = null;
|
||||
#revn = 0;
|
||||
#selection = signal<PenpotShape[]>([]);
|
||||
form = new FormGroup({
|
||||
name: new FormControl(''),
|
||||
});
|
||||
theme = signal('');
|
||||
projectName = signal('Unknown');
|
||||
|
||||
constructor() {
|
||||
window.addEventListener('message', (event) => {
|
||||
if (event.data.type === 'file') {
|
||||
this.#fileId = event.data.content.id;
|
||||
this.#revn = event.data.content.revn;
|
||||
} else if (event.data.type === 'page') {
|
||||
this.#refreshPage(
|
||||
event.data.content.page.id,
|
||||
event.data.content.page.name
|
||||
);
|
||||
} else if (event.data.type === 'selection') {
|
||||
this.#refreshSelection(event.data.content.selection);
|
||||
} else if (event.data.type === 'init') {
|
||||
this.#fileId = event.data.content.fileId;
|
||||
this.#revn = event.data.content.revn;
|
||||
this.#refreshPage(event.data.content.pageId, event.data.content.name);
|
||||
this.#refreshSelection(event.data.content.selection);
|
||||
this.theme.set(event.data.content.theme);
|
||||
} else if (event.data.type === 'theme') {
|
||||
this.theme.set(event.data.content);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
close() {
|
||||
this.#sendMessage({ content: 'close' });
|
||||
}
|
||||
|
||||
updateName() {
|
||||
const id = this.#selection()[0].id;
|
||||
const name = this.form.get('name')?.value;
|
||||
this.#sendMessage({ content: 'change-name', data: { id, name } });
|
||||
}
|
||||
|
||||
createRect() {
|
||||
this.#sendMessage({ content: 'create-rect' });
|
||||
}
|
||||
|
||||
moveX() {
|
||||
const id = this.#selection()[0].id;
|
||||
this.#sendMessage({ content: 'move-x', data: { id } });
|
||||
}
|
||||
|
||||
moveY() {
|
||||
const id = this.#selection()[0].id;
|
||||
this.#sendMessage({ content: 'move-y', data: { id } });
|
||||
}
|
||||
|
||||
resizeW() {
|
||||
const id = this.#selection()[0].id;
|
||||
this.#sendMessage({ content: 'resize-w', data: { id } });
|
||||
}
|
||||
|
||||
resizeH() {
|
||||
const id = this.#selection()[0].id;
|
||||
this.#sendMessage({ content: 'resize-h', data: { id } });
|
||||
}
|
||||
|
||||
loremIpsum() {
|
||||
this.#sendMessage({ content: 'lorem-ipsum' });
|
||||
}
|
||||
|
||||
#sendMessage(message: unknown) {
|
||||
parent.postMessage(message, '*');
|
||||
}
|
||||
|
||||
#refreshPage(pageId: string, name: string) {
|
||||
this.#pageId = pageId;
|
||||
|
||||
this.projectName.set(name || 'Unknown');
|
||||
}
|
||||
|
||||
#refreshSelection(selection: PenpotShape[]) {
|
||||
this.#selection.set(selection);
|
||||
if (selection && selection.length > 0) {
|
||||
this.form.get('name')?.setValue(this.#selection()[0].name);
|
||||
} else {
|
||||
this.form.get('name')?.setValue('');
|
||||
}
|
||||
}
|
||||
}
|
9
apps/poc-state-plugin/src/assets/manifest.json
Normal file
9
apps/poc-state-plugin/src/assets/manifest.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"name": "POC State Read",
|
||||
"code": "http://localhost:4202/assets/plugin.js",
|
||||
"permissions": [
|
||||
"page:read",
|
||||
"file:read",
|
||||
"selection:read"
|
||||
]
|
||||
}
|
BIN
apps/poc-state-plugin/src/favicon.ico
Normal file
BIN
apps/poc-state-plugin/src/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
13
apps/poc-state-plugin/src/index.html
Normal file
13
apps/poc-state-plugin/src/index.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>poc-state-plugin</title>
|
||||
<base href="/" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
4
apps/poc-state-plugin/src/main.ts
Normal file
4
apps/poc-state-plugin/src/main.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent).catch((err) => console.error(err));
|
113
apps/poc-state-plugin/src/plugin.ts
Normal file
113
apps/poc-state-plugin/src/plugin.ts
Normal file
|
@ -0,0 +1,113 @@
|
|||
import { PenpotText } from '@penpot/plugin-types';
|
||||
|
||||
penpot.ui.open('Plugin name', 'http://localhost:4202', {
|
||||
width: 500,
|
||||
height: 600,
|
||||
});
|
||||
|
||||
penpot.ui.onMessage<{ content: string; data: unknown }>((message) => {
|
||||
if (message.content === 'close') {
|
||||
penpot.closePlugin();
|
||||
} else if (message.content === 'ready') {
|
||||
const page = penpot.getPage();
|
||||
const file = penpot.getFile();
|
||||
|
||||
if (!page || !file) {
|
||||
return;
|
||||
}
|
||||
|
||||
penpot.ui.sendMessage({
|
||||
type: 'init',
|
||||
content: {
|
||||
name: page.name,
|
||||
pageId: page.id,
|
||||
fileId: file.id,
|
||||
revn: file.revn,
|
||||
theme: penpot.getTheme(),
|
||||
selection: penpot.getSelectedShapes(),
|
||||
},
|
||||
});
|
||||
} else if (message.content === 'change-name') {
|
||||
const shape = penpot
|
||||
.getPage()
|
||||
?.getShapeById('' + (message.data as { id: string }).id);
|
||||
if (shape) {
|
||||
shape.name = (message.data as { name: string }).name;
|
||||
}
|
||||
} else if (message.content === 'create-rect') {
|
||||
const shape = penpot.createRectangle();
|
||||
penpot.log(shape);
|
||||
} else if (message.content === 'move-x') {
|
||||
const shape = penpot
|
||||
.getPage()
|
||||
?.getShapeById('' + (message.data as { id: string }).id);
|
||||
if (shape) {
|
||||
shape.x += 100;
|
||||
}
|
||||
} else if (message.content === 'move-y') {
|
||||
const shape = penpot
|
||||
.getPage()
|
||||
?.getShapeById('' + (message.data as { id: string }).id);
|
||||
if (shape) {
|
||||
shape.y += 100;
|
||||
}
|
||||
} else if (message.content === 'resize-w') {
|
||||
const shape = penpot
|
||||
.getPage()
|
||||
?.getShapeById('' + (message.data as { id: string }).id);
|
||||
if (shape) {
|
||||
shape.resize(shape.width * 2, shape.height);
|
||||
}
|
||||
} else if (message.content === 'resize-h') {
|
||||
const shape = penpot
|
||||
.getPage()
|
||||
?.getShapeById('' + (message.data as { id: string }).id);
|
||||
if (shape) {
|
||||
shape.resize(shape.width, shape.height * 2);
|
||||
}
|
||||
} else if (message.content === 'lorem-ipsum') {
|
||||
const selection = penpot.selection;
|
||||
|
||||
for (const shape of selection) {
|
||||
(
|
||||
shape as PenpotText
|
||||
).characters = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam id mauris ut felis finibus congue. Ut odio ipsum, condimentum id tellus sit amet, dapibus sagittis ligula. Pellentesque hendrerit, nulla sit amet aliquet scelerisque, orci nunc commodo tellus, quis hendrerit nisl massa non tellus.
|
||||
|
||||
Phasellus fringilla tortor elit, ac dictum tellus posuere sodales. Ut eget imperdiet ante. Nunc eros magna, tincidunt non finibus in, tempor elementum nunc. Sed commodo magna in arcu aliquam efficitur.`;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
penpot.on('pagechange', () => {
|
||||
const page = penpot.getPage();
|
||||
const shapes = page?.findShapes();
|
||||
|
||||
penpot.ui.sendMessage({
|
||||
type: 'page',
|
||||
content: { page, shapes },
|
||||
});
|
||||
});
|
||||
|
||||
penpot.on('filechange', () => {
|
||||
const file = penpot.getFile();
|
||||
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
penpot.ui.sendMessage({
|
||||
type: 'file',
|
||||
content: {
|
||||
id: file.id,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
penpot.on('selectionchange', () => {
|
||||
const selection = penpot.getSelectedShapes();
|
||||
penpot.ui.sendMessage({ type: 'selection', content: { selection } });
|
||||
});
|
||||
|
||||
penpot.on('themechange', (theme) => {
|
||||
penpot.ui.sendMessage({ type: 'theme', content: theme });
|
||||
});
|
1
apps/poc-state-plugin/src/styles.css
Normal file
1
apps/poc-state-plugin/src/styles.css
Normal file
|
@ -0,0 +1 @@
|
|||
/* You can add global styles to this file, and also import other style files */
|
10
apps/poc-state-plugin/tsconfig.app.json
Normal file
10
apps/poc-state-plugin/tsconfig.app.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": []
|
||||
},
|
||||
"files": ["src/main.ts"],
|
||||
"include": ["src/**/*.d.ts"],
|
||||
"exclude": ["src/**/*.test.ts", "src/**/*.spec.ts"]
|
||||
}
|
7
apps/poc-state-plugin/tsconfig.editor.json
Normal file
7
apps/poc-state-plugin/tsconfig.editor.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": ["src/**/*.ts"],
|
||||
"compilerOptions": {
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
33
apps/poc-state-plugin/tsconfig.json
Normal file
33
apps/poc-state-plugin/tsconfig.json
Normal file
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2022",
|
||||
"useDefineForClassFields": false,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.editor.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.plugin.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
8
apps/poc-state-plugin/tsconfig.plugin.json
Normal file
8
apps/poc-state-plugin/tsconfig.plugin.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"types": []
|
||||
},
|
||||
"files": ["src/plugin.ts"],
|
||||
"include": ["../../libs/plugin-types/index.d.ts"],
|
||||
}
|
117
docs/create-angular-plugin.md
Normal file
117
docs/create-angular-plugin.md
Normal file
|
@ -0,0 +1,117 @@
|
|||
# Creating a Plugin
|
||||
|
||||
This guide walks you through the steps to create an Angular plugin.
|
||||
|
||||
### Step 1: Initialize the Plugin
|
||||
|
||||
First, you need to create the scaffolding for your plugin. Use the following command, replacing `example-plugin` with the name of your plugin:
|
||||
|
||||
```sh
|
||||
npx nx g @nx/angular:app example-plugin --directory=apps/example-plugin
|
||||
```
|
||||
|
||||
### Step 2: Configure the Manifest
|
||||
|
||||
Next, create a `manifest.json` file inside the `/src/assets` directory. This file is crucial as it defines key properties of your plugin, including permissions and the entry point script.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Example plugin",
|
||||
"code": "http://localhost:4202/assets/plugin.js",
|
||||
"permissions": ["page:read", "file:read", "selection:read"]
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Update Project Configuration
|
||||
|
||||
Now, add the following configuration to your `project.json` to compile the `plugin.ts` file:
|
||||
|
||||
```typescript
|
||||
"targets": {
|
||||
"buildPlugin": {
|
||||
"executor": "@nx/esbuild:esbuild",
|
||||
"outputs": [
|
||||
"{options.outputPath}"
|
||||
],
|
||||
"options": {
|
||||
"minify": true,
|
||||
"outputPath": "apps/example-plugin/src/assets/",
|
||||
"main": "apps/example-plugin/src/plugin.ts",
|
||||
"tsConfig": "apps/example-plugin/tsconfig.plugin.json",
|
||||
"generatePackageJson": false,
|
||||
"format": [
|
||||
"esm"
|
||||
],
|
||||
"deleteOutputPath": false
|
||||
}
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Also, update `targets.build` with the following code to allow the use of Penpot styles.
|
||||
|
||||
```json
|
||||
"styles": [
|
||||
"libs/plugins-styles/src/lib/styles.css",
|
||||
"apps/example-plugin/src/styles.css"
|
||||
],
|
||||
"optimization": {
|
||||
"scripts": true,
|
||||
"styles": true,
|
||||
"fonts": false
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Modify TypeScript Configuration
|
||||
|
||||
Create ``tsconfig.plugin.json` next to the `tsconfig.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"types": []
|
||||
},
|
||||
"files": ["src/plugin.ts"],
|
||||
"include": ["../../libs/plugin-types/index.d.ts"]
|
||||
}
|
||||
```
|
||||
|
||||
Add the reference to the main tsconfig.json:
|
||||
|
||||
```json
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.plugin.json"
|
||||
}
|
||||
],
|
||||
```
|
||||
|
||||
### Step 5: Run the plugin
|
||||
|
||||
Run this command:
|
||||
|
||||
```sh
|
||||
npx nx run-many --targets=buildPlugin,serve --projects=poc-state-plugin --watch
|
||||
```
|
||||
|
||||
This will run two tasks: `serve`, the usual Angular server, and `buildPlugin`, which will compile the `plugin.ts` file.
|
||||
|
||||
### Step 6: Load the Plugin in Penpot
|
||||
|
||||
Finally, to load your plugin into Penpot, execute the following command in the browser's console devtools:
|
||||
|
||||
```typescript
|
||||
ɵloadPlugin({ manifest: 'http://localhost:4202/manifest.json' });
|
||||
```
|
||||
|
||||
### Learn More About Plugin Development
|
||||
|
||||
For more detailed information on plugin development, check out our guides:
|
||||
|
||||
- [Plugin Usage Documentation](docs/plugin-usage.md)
|
||||
- [Create API Documentation](docs/create-api.md)
|
||||
|
||||
### Using a Starter Template
|
||||
|
||||
If you prefer to kickstart your plugin development, consider using the [Penpot Plugin Starter Template](https://github.com/penpot/penpot-plugin-starter-template). It's a template designed to streamline the creation process for Penpot plugins.
|
11
nx.json
11
nx.json
|
@ -21,6 +21,11 @@
|
|||
"cache": true,
|
||||
"dependsOn": ["^build"],
|
||||
"inputs": ["default", "^default"]
|
||||
},
|
||||
"@angular-devkit/build-angular:application": {
|
||||
"cache": true,
|
||||
"dependsOn": ["^build"],
|
||||
"inputs": ["default", "^default"]
|
||||
}
|
||||
},
|
||||
"plugins": [
|
||||
|
@ -47,6 +52,12 @@
|
|||
"linter": "eslint",
|
||||
"unitTestRunner": "vitest",
|
||||
"e2eTestRunner": "none"
|
||||
},
|
||||
"@nx/angular:application": {
|
||||
"e2eTestRunner": "none",
|
||||
"linter": "eslint",
|
||||
"style": "css",
|
||||
"unitTestRunner": "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
9320
package-lock.json
generated
9320
package-lock.json
generated
File diff suppressed because it is too large
Load diff
25
package.json
25
package.json
|
@ -6,6 +6,7 @@
|
|||
"start": "npx nx run plugins-runtime:build --watch --mode development & npx nx run plugins-runtime:preview",
|
||||
"start:example": "npx nx run example-plugin:build --watch & npx nx run example-plugin:preview",
|
||||
"start:read-plugin": "npx nx run poc-state-read-plugin:build --watch & npx nx run poc-state-read-plugin:preview",
|
||||
"start:pc-plugin": "npx nx run-many --targets=buildPlugin,serve --projects=poc-state-plugin --watch",
|
||||
"start:contrast-plugin": "npx nx run contrast-plugin:build --watch & npx nx run contrast-plugin:preview",
|
||||
"start:rpc-api": "npx nx serve rpc-api",
|
||||
"start:styles-example": "npx nx run example-styles:serve --port 4202",
|
||||
|
@ -19,9 +20,19 @@
|
|||
},
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~17.1.0",
|
||||
"@angular-devkit/core": "~17.1.0",
|
||||
"@angular-devkit/schematics": "~17.1.0",
|
||||
"@angular-eslint/eslint-plugin": "~17.0.0",
|
||||
"@angular-eslint/eslint-plugin-template": "~17.0.0",
|
||||
"@angular-eslint/template-parser": "~17.0.0",
|
||||
"@angular/cli": "~17.1.0",
|
||||
"@angular/compiler-cli": "~17.1.0",
|
||||
"@angular/language-service": "~17.1.0",
|
||||
"@commitlint/cli": "^18.6.0",
|
||||
"@commitlint/config-conventional": "^18.6.0",
|
||||
"@fastify/cors": "^9.0.1",
|
||||
"@nx/angular": "^18.0.2",
|
||||
"@nx/esbuild": "18.0.2",
|
||||
"@nx/eslint": "18.0.2",
|
||||
"@nx/eslint-plugin": "18.0.2",
|
||||
|
@ -29,6 +40,7 @@
|
|||
"@nx/node": "^18.0.2",
|
||||
"@nx/vite": "18.0.2",
|
||||
"@nx/web": "18.0.2",
|
||||
"@schematics/angular": "~17.1.0",
|
||||
"@swc-node/register": "~1.6.7",
|
||||
"@swc/core": "~1.3.85",
|
||||
"@swc/helpers": "~0.5.2",
|
||||
|
@ -45,6 +57,7 @@
|
|||
"happy-dom": "^13.6.2",
|
||||
"husky": "^9.0.10",
|
||||
"jsdom": "~22.1.0",
|
||||
"jsonc-eslint-parser": "^2.1.0",
|
||||
"nx": "18.0.2",
|
||||
"prettier": "^2.6.2",
|
||||
"swc-loader": "0.1.15",
|
||||
|
@ -59,16 +72,26 @@
|
|||
"packages/*"
|
||||
],
|
||||
"dependencies": {
|
||||
"@angular/animations": "~17.1.0",
|
||||
"@angular/common": "~17.1.0",
|
||||
"@angular/compiler": "~17.1.0",
|
||||
"@angular/core": "~17.1.0",
|
||||
"@angular/forms": "~17.1.0",
|
||||
"@angular/platform-browser": "~17.1.0",
|
||||
"@angular/platform-browser-dynamic": "~17.1.0",
|
||||
"@angular/router": "~17.1.0",
|
||||
"@fastify/autoload": "~5.7.1",
|
||||
"@fastify/sensible": "~5.2.0",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"axios": "^1.6.0",
|
||||
"fastify": "~4.13.0",
|
||||
"fastify-plugin": "~4.5.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"ses": "^1.1.0",
|
||||
"tslib": "^2.3.0",
|
||||
"uuid": "^9.0.1",
|
||||
"zod": "^3.22.4"
|
||||
"zod": "^3.22.4",
|
||||
"zone.js": "~0.14.3"
|
||||
},
|
||||
"nx": {
|
||||
"includedScripts": []
|
||||
|
|
Loading…
Add table
Reference in a new issue