Compare commits

...

5 Commits

Author SHA1 Message Date
Juanfran
40eb002262 🎉 Add UI Typescript example component 2026-01-23 13:28:21 +01:00
Juanfran
e9cf870028 🐛 Fix plugins ui lint 2026-01-23 13:06:33 +01:00
Juanfran
acb8ae1334 🐛 Add missing dependency 2026-01-23 13:05:42 +01:00
Juanfran
824554b4f2 🎉 Add ts ui storybook 2026-01-23 13:05:02 +01:00
Juanfran
f02e06fca1 ⬆️ Update plugins dependencies 2026-01-23 13:04:11 +01:00
77 changed files with 11542 additions and 14805 deletions

View File

@@ -50,6 +50,7 @@
"@penpot/plugins-runtime": "1.4.2",
"@penpot/svgo": "penpot/svgo#v3.2",
"@penpot/text-editor": "workspace:./text-editor",
"@penpot/ui": "workspace:../plugins/libs/ui/dist",
"@playwright/test": "1.57.0",
"@storybook/addon-docs": "10.1.11",
"@storybook/addon-themes": "10.1.11",

View File

@@ -28,6 +28,9 @@ importers:
'@penpot/text-editor':
specifier: workspace:./text-editor
version: link:text-editor
'@penpot/ui':
specifier: workspace:../plugins/libs/ui/dist
version: link:../plugins/libs/ui/dist
'@playwright/test':
specifier: 1.57.0
version: 1.57.0

View File

@@ -18,6 +18,7 @@
<meta name="twitter:creator" content="@penpotapp">
<meta name="theme-color" content="#FFFFFF" media="(prefers-color-scheme: light)">
<link id="theme" href="css/main.css?version={{& version_tag}}" rel="stylesheet" type="text/css" />
<link href="css/ui.css?ts={{& ts}}" rel="stylesheet" type="text/css" />
{{#isDebug}}
<link href="css/debug.css?version={{& version_tag}}" rel="stylesheet" type="text/css" />
{{/isDebug}}

View File

@@ -7,6 +7,7 @@
(ns app.main.ui.dashboard
(:require-macros [app.main.style :as stl])
(:require
["@penpot/ui" :refer [Example]]
[app.common.data.macros :as dm]
[app.config :as cf]
[app.main.data.dashboard :as dd]
@@ -312,6 +313,9 @@
;; team is already set so don't put the team into mf/deps.
[:main {:class (stl/css :dashboard)
:key (dm/str (:id team))}
[:div
[:> Example]]
[:> sidebar*
{:team team
:projects projects

4
plugins/.gitignore vendored
View File

@@ -53,4 +53,6 @@ apps/e2e/screenshots/*.png
vite.config.*.timestamp*
vitest.config.*.timestamp*
.pnpm-store
.pnpm-store
storybook-static

View File

@@ -1 +0,0 @@
pnpx --no -- commitlint --edit $1

View File

@@ -1,5 +0,0 @@
#!/bin/bash
if [ -z "$HUSKY_HOOK" ] || [ "$HUSKY_HOOK" = "pre-commit" ]; then
pnpm run lint:affected
fi

View File

@@ -1,5 +0,0 @@
#!/bin/bash
if [ "$HUSKY_HOOK" = "pre-push" ]; then
pnpm run lint:affected
fi

View File

@@ -1,8 +1,7 @@
{
"recommendations": [
"nrwl.angular-console",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"firsttris.vscode-jest-runner"
"prettier.prettier-vscode",
"dbaeumer.vscode-eslint"
]
}

View File

@@ -1,3 +1,5 @@
{
"prettier.singleQuote": true
"prettier.singleQuote": true,
"editor.defaultFormatter": "prettier.prettier-vscode",
"editor.formatOnSave": true
}

View File

@@ -1,134 +0,0 @@
# Contributing Guide
Thank you for your interest in contributing to Penpot Plugins. This is a
generic guide that details how to contribute to Penpot Plugins in a way that
is efficient for everyone. If you want a specific documentation for
different parts of the platform, please refer to `docs/` directory.
## Reporting Bugs
We are using [GitHub Issues](https://github.com/penpot/penpot-plugins/issues)
for our public bugs. We keep a close eye on this and try to make it
clear when we have an internal fix in progress. Before filing a new
task, try to make sure your problem doesn't already exist.
If you found a bug, please report it, as far as possible with:
- a detailed explanation of steps to reproduce the error
- a browser and the browser version used
- a dev tools console exception stack trace (if it is available)
If you found a bug that you consider better discuss in private (for
example: security bugs), consider first send an email to
`support@penpot.app`.
**We don't have formal bug bounty program for security reports; this
is an open source application and your contribution will be recognized
in the changelog.**
## Pull requests
If you want propose a change or bug fix with the Pull-Request system
firstly you should carefully read the **DCO** section and format your
commits accordingly.
If you intend to fix a bug it's fine to submit a pull request right
away but we still recommend to file an issue detailing what you're
fixing. This is helpful in case we don't accept that specific fix but
want to keep track of the issue.
If you want to implement or start working in a new feature, please
open a **question** / **discussion** issue for it. No pull-request
will be accepted without previous chat about the changes,
independently if it is a new feature, already planned feature or small
quick win.
If is going to be your first pull request, You can learn how from this
free video series:
https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github
We will use the `easy fix` mark for tag for indicate issues that are
easy for beginners.
## Commit Guidelines
To maintain a clear and organized commit history in this repository, we adhere to the Conventional Commits specification. Conventional Commits provide a structured format for commit messages, making it easier to track changes, automate versioning, and improve readability.
Please familiarize yourself with the Conventional Commits rules by visiting the [official Conventional Commits website](https://www.conventionalcommits.org/en/v1.0.0/). This specification outlines how to structure your commit messages, including types, scopes, and descriptions.
## Code of conduct
As contributors and maintainers of this project, we pledge to respect
all people who contribute through reporting issues, posting feature
requests, updating documentation, submitting pull requests or patches,
and other activities.
We are committed to making participation in this project a
harassment-free experience for everyone, regardless of level of
experience, gender, gender identity and expression, sexual
orientation, disability, personal appearance, body size, race,
ethnicity, age, or religion.
Examples of unacceptable behavior by participants include the use of
sexual language or imagery, derogatory comments or personal attacks,
trolling, public or private harassment, insults, or other
unprofessional conduct.
Project maintainers have the right and responsibility to remove, edit,
or reject comments, commits, code, wiki edits, issues, and other
contributions that are not aligned to this Code of Conduct. Project
maintainers who do not follow the Code of Conduct may be removed from
the project team.
This code of conduct applies both within project spaces and in public
spaces when an individual is representing the project or its
community.
Instances of abusive, harassing, or otherwise unacceptable behavior
may be reported by opening an issue or contacting one or more of the
project maintainers.
This Code of Conduct is adapted from the Contributor Covenant, version
1.1.0, available from http://contributor-covenant.org/version/1/1/0/
## Developer's Certificate of Origin (DCO)
By submitting code you are agree and can certify the below:
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
Then, all your code patches (**documentation are excluded**) should
contain a sign-off at the end of the patch/commit description body. It
can be automatically added on adding `-s` parameter to `git commit`.
This is an example of the aspect of the line:
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Please, use your real name (sorry, no pseudonyms or anonymous
contributions are allowed).

View File

@@ -67,7 +67,8 @@
"port": 4308
}
},
"defaultConfiguration": "development"
"defaultConfiguration": "development",
"continuous": true
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",

View File

@@ -1,7 +1,9 @@
import { provideZoneChangeDetection } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, appConfig).catch((err) =>
console.error(err),
);
bootstrapApplication(AppComponent, {
...appConfig,
providers: [provideZoneChangeDetection(), ...appConfig.providers],
}).catch((err) => console.error(err));

View File

@@ -2,7 +2,8 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": []
"types": [],
"moduleResolution": "bundler"
},
"files": ["src/main.ts"],
"include": ["src/**/*.d.ts"],

View File

@@ -8,7 +8,9 @@
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
"noFallthroughCasesInSwitch": true,
"moduleResolution": "bundler",
"module": "preserve"
},
"files": [],
"include": [],

View File

@@ -1,7 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"types": []
"types": [],
"moduleResolution": "bundler"
},
"files": ["src/plugin.ts"],
"include": ["../../libs/plugin-types/index.d.ts"]

View File

@@ -8,7 +8,9 @@
"vite/client",
"node",
"vitest"
]
],
"moduleResolution": "bundler",
"isolatedModules": true
},
"include": [
"vite.config.ts",

View File

@@ -67,7 +67,8 @@
"port": 4302
}
},
"defaultConfiguration": "development"
"defaultConfiguration": "development",
"continuous": true
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",

View File

@@ -1,7 +1,9 @@
import { provideZoneChangeDetection } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, appConfig).catch((err) =>
console.error(err),
);
bootstrapApplication(AppComponent, {
...appConfig,
providers: [provideZoneChangeDetection(), ...appConfig.providers],
}).catch((err) => console.error(err));

View File

@@ -2,7 +2,8 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": []
"types": [],
"moduleResolution": "bundler"
},
"files": ["src/main.ts"],
"include": ["src/**/*.d.ts"],

View File

@@ -8,7 +8,9 @@
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
"noFallthroughCasesInSwitch": true,
"moduleResolution": "bundler",
"module": "preserve"
},
"files": [],
"include": [],

View File

@@ -1,7 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"types": []
"types": [],
"moduleResolution": "bundler"
},
"files": ["src/plugin.ts"],
"include": ["../../libs/plugin-types/index.d.ts"]

View File

@@ -67,7 +67,8 @@
"port": 4303
}
},
"defaultConfiguration": "development"
"defaultConfiguration": "development",
"continuous": true
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",

View File

@@ -1,7 +1,9 @@
import { provideZoneChangeDetection } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, appConfig).catch((err) =>
console.error(err),
);
bootstrapApplication(AppComponent, {
...appConfig,
providers: [provideZoneChangeDetection(), ...appConfig.providers],
}).catch((err) => console.error(err));

View File

@@ -2,7 +2,8 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": []
"types": [],
"moduleResolution": "bundler"
},
"files": ["src/main.ts"],
"include": ["src/**/*.d.ts"],

View File

@@ -8,7 +8,9 @@
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
"noFallthroughCasesInSwitch": true,
"moduleResolution": "bundler",
"module": "preserve"
},
"files": [],
"include": [],

View File

@@ -1,7 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"types": []
"types": [],
"moduleResolution": "bundler"
},
"files": ["src/plugin.ts"],
"include": ["../../libs/plugin-types/index.d.ts"]

View File

@@ -67,7 +67,8 @@
"port": 4304
}
},
"defaultConfiguration": "development"
"defaultConfiguration": "development",
"continuous": true
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",

View File

@@ -1,7 +1,9 @@
import { provideZoneChangeDetection } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, appConfig).catch((err) =>
console.error(err),
);
bootstrapApplication(AppComponent, {
...appConfig,
providers: [provideZoneChangeDetection(), ...appConfig.providers],
}).catch((err) => console.error(err));

View File

@@ -2,7 +2,8 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": []
"types": [],
"moduleResolution": "bundler"
},
"files": ["src/main.ts"],
"include": ["src/**/*.d.ts"],

View File

@@ -8,7 +8,9 @@
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
"noFallthroughCasesInSwitch": true,
"moduleResolution": "bundler",
"module": "preserve"
},
"files": [],
"include": [],

View File

@@ -1,7 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"types": []
"types": [],
"moduleResolution": "bundler"
},
"files": ["src/plugin.ts"],
"include": ["../../libs/plugin-types/index.d.ts"]

View File

@@ -67,7 +67,8 @@
"host": "0.0.0.0"
}
},
"defaultConfiguration": "development"
"defaultConfiguration": "development",
"continuous": true
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",

View File

@@ -1,3 +1,4 @@
/* eslint-disable */
import { Component, effect, signal } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import type { Shape } from '@penpot/plugin-types';

View File

@@ -1,4 +1,7 @@
import { provideZoneChangeDetection } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
bootstrapApplication(AppComponent, {
providers: [provideZoneChangeDetection()],
}).catch((err) => console.error(err));

View File

@@ -2,7 +2,8 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": []
"types": [],
"moduleResolution": "bundler"
},
"files": ["src/main.ts"],
"include": ["src/**/*.d.ts"],

View File

@@ -8,7 +8,9 @@
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
"noFallthroughCasesInSwitch": true,
"moduleResolution": "bundler",
"module": "preserve"
},
"files": [],
"include": [],

View File

@@ -1,7 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"types": []
"types": [],
"moduleResolution": "bundler"
},
"files": ["src/plugin.ts"],
"include": ["../../libs/plugin-types/index.d.ts"]

View File

@@ -67,7 +67,8 @@
"port": 4307
}
},
"defaultConfiguration": "development"
"defaultConfiguration": "development",
"continuous": true
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",

View File

@@ -38,107 +38,125 @@
</button>
</div>
<div *ngIf="tab === 'add'" class="form">
<p class="explanation body-s">
Select layers to rename (otherwise it will apply to all layers) and enter
the text you want to add.
</p>
<div class="form-group">
<label class="input-label-hidden" for="search">Add text</label>
<input
#addElement
[(ngModel)]="addText"
class="input"
type="text"
placeholder="[Original layer name]"
id="addText"
(keydown.enter)="updateText()"
/>
@if (tab === 'add') {
<div class="form">
<p class="explanation body-s">
Select layers to rename (otherwise it will apply to all layers) and
enter the text you want to add.
</p>
<div class="form-group">
<label class="input-label-hidden" for="search">Add text</label>
<input
#addElement
[(ngModel)]="addText"
class="input"
type="text"
placeholder="[Original layer name]"
id="addText"
(keydown.enter)="updateText()"
/>
</div>
<button
type="button"
data-appearance="primary"
[class.btn-feedback]="btnFeedback"
[disabled]="addText === '[Original layer name]' || !addText"
(click)="updateText()"
>
@if (!btnFeedback) {
<span class="text-btn">Add</span>
}
@if (btnFeedback) {
<span class="icon icon-btn">
<svg viewBox="0 0 16 16" fill="none">
<path d="M13.333 4 6 11.333 2.667 8" class="stroke" />
</svg>
</span>
}
</button>
<p class="body-s preview">Previsualization:</p>
<ul class="preview-list">
@for (preview of previewList(); track preview) {
<li class="preview-item">
<span class="original" [title]="preview.name">{{
preview.name
}}</span>
<ng-template *ngTemplateOutlet="iconArrowRight"></ng-template>
<span class="result" [title]="resultAddText(preview)">{{
resultAddText(preview)
}}</span>
</li>
}
</ul>
</div>
<button
type="button"
data-appearance="primary"
[class.btn-feedback]="btnFeedback"
[disabled]="addText === '[Original layer name]' || !addText"
(click)="updateText()"
>
<span *ngIf="!btnFeedback" class="text-btn">Add</span>
<span *ngIf="btnFeedback" class="icon icon-btn">
<svg viewBox="0 0 16 16" fill="none">
<path d="M13.333 4 6 11.333 2.667 8" class="stroke" />
</svg>
</span>
</button>
<p class="body-s preview">Previsualization:</p>
<ul class="preview-list">
<li class="preview-item" *ngFor="let preview of previewList()">
<span class="original" [title]="preview.name">{{ preview.name }}</span>
<ng-template *ngTemplateOutlet="iconArrowRight"></ng-template>
<span class="result" [title]="resultAddText(preview)">{{
resultAddText(preview)
}}</span>
</li>
</ul>
</div>
}
<div *ngIf="tab === 'replace'" class="form">
<p class="explanation body-s">
Select layers to rename (otherwise it will apply to all layers) and enter
the replacement text.
</p>
<div class="form-group">
<label class="input-label-hidden" for="search">Search</label>
<input
[(ngModel)]="textToReplace.search"
class="input"
type="text"
placeholder="Search"
id="search"
#searchElement
(keydown)="previewReplace()"
/>
@if (tab === 'replace') {
<div class="form">
<p class="explanation body-s">
Select layers to rename (otherwise it will apply to all layers) and
enter the replacement text.
</p>
<div class="form-group">
<label class="input-label-hidden" for="search">Search</label>
<input
[(ngModel)]="textToReplace.search"
class="input"
type="text"
placeholder="Search"
id="search"
#searchElement
(keydown)="previewReplace()"
/>
</div>
<div class="form-group">
<label class="input-label-hidden" for="replace">Replace</label>
<input
[(ngModel)]="textToReplace.replace"
class="input"
type="text"
placeholder="Write the new text"
id="replace"
(keydown.enter)="updateText()"
/>
</div>
<button
type="button"
data-appearance="primary"
(click)="updateText()"
[class.btn-feedback]="btnFeedback"
[disabled]="!textToReplace.search"
>
@if (!btnFeedback) {
<span class="text-btn">Replace</span>
}
@if (btnFeedback) {
<span class="icon icon-btn icon-tick">
<svg width="16" height="16" fill="none">
<path d="M13.333 4 6 11.333 2.667 8" class="stroke" />
</svg>
</span>
}
</button>
<p class="body-s preview">Previsualization:</p>
<ul class="preview-list replace">
@if (previewList().length === 0) {
<li class="no-match">No matches found</li>
}
@for (preview of previewList(); track preview) {
<li class="preview-item">
<span
class="original"
[innerHTML]="highlightMatch(preview.name)"
[title]="preview.name"
></span>
<ng-template *ngTemplateOutlet="iconArrowRight"></ng-template>
<span class="result" [title]="resultReplaceText(preview.name)">{{
resultReplaceText(preview.name)
}}</span>
</li>
}
</ul>
</div>
<div class="form-group">
<label class="input-label-hidden" for="replace">Replace</label>
<input
[(ngModel)]="textToReplace.replace"
class="input"
type="text"
placeholder="Write the new text"
id="replace"
(keydown.enter)="updateText()"
/>
</div>
<button
type="button"
data-appearance="primary"
(click)="updateText()"
[class.btn-feedback]="btnFeedback"
[disabled]="!textToReplace.search"
>
<span *ngIf="!btnFeedback" class="text-btn">Replace</span>
<span *ngIf="btnFeedback" class="icon icon-btn icon-tick">
<svg width="16" height="16" fill="none">
<path d="M13.333 4 6 11.333 2.667 8" class="stroke" />
</svg>
</span>
</button>
<p class="body-s preview">Previsualization:</p>
<ul class="preview-list replace">
<li *ngIf="previewList().length === 0" class="no-match">
No matches found
</li>
<li class="preview-item" *ngFor="let preview of previewList()">
<span
class="original"
[innerHTML]="highlightMatch(preview.name)"
[title]="preview.name"
></span>
<ng-template *ngTemplateOutlet="iconArrowRight"></ng-template>
<span class="result" [title]="resultReplaceText(preview.name)">{{
resultReplaceText(preview.name)
}}</span>
</li>
</ul>
</div>
}
</div>

View File

@@ -1,7 +1,9 @@
import { provideZoneChangeDetection } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, appConfig).catch((err) =>
console.error(err),
);
bootstrapApplication(AppComponent, {
...appConfig,
providers: [provideZoneChangeDetection(), ...appConfig.providers],
}).catch((err) => console.error(err));

View File

@@ -2,7 +2,8 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": []
"types": [],
"moduleResolution": "bundler"
},
"files": ["src/main.ts"],
"include": ["src/**/*.d.ts"],

View File

@@ -8,7 +8,9 @@
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
"noFallthroughCasesInSwitch": true,
"moduleResolution": "bundler",
"module": "preserve"
},
"files": [],
"include": [],

View File

@@ -1,7 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"types": []
"types": [],
"moduleResolution": "bundler"
},
"files": ["src/plugin.ts"],
"include": ["../../libs/plugin-types/index.d.ts"]

View File

@@ -66,7 +66,8 @@
"port": 4306
}
},
"defaultConfiguration": "development"
"defaultConfiguration": "development",
"continuous": true
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",

View File

@@ -1,17 +1,19 @@
<div [formGroup]="form">
<p class="text body-s">Import a data file (CSV)</p>
<div class="error" *ngIf="fileError">
<img
class="close-icon"
(click)="clearError()"
src="../assets/close.svg"
alt="close error"
/>
<span class="message body-s"
>Something was wrong. <br />
Make sure the formst is .csv</span
>
</div>
@if (fileError) {
<div class="error">
<img
class="close-icon"
(click)="clearError()"
src="../assets/close.svg"
alt="close error"
/>
<span class="message body-s"
>Something was wrong. <br />
Make sure the formst is .csv</span
>
</div>
}
<div class="input-container">
<input
type="file"
@@ -25,9 +27,11 @@
<hr />
<div class="new-table">
<p class="text body-s">Or create a new table</p>
<span *ngIf="selectedColumn && selectedRow !== 0" class="tag"
>{{ selectedRow }} rows x {{ selectedColumn }} cols</span
>
@if (selectedColumn && selectedRow !== 0) {
<span class="tag"
>{{ selectedRow }} rows x {{ selectedColumn }} cols</span
>
}
</div>
<div class="checkbox-container">
@@ -73,21 +77,22 @@
</div>
<div class="table-grid">
<span
class="cell"
*ngFor="let cell of cells; index as i"
(mouseenter)="setColRow(i)"
(mouseleave)="clearColRow()"
(click)="createTable(i)"
>
@for (cell of cells; track cell; let i = $index) {
<span
class="square"
[class.active]="
selectedCell &&
selectedCell.column >= getCellColRow(i).column &&
selectedCell.row >= getCellColRow(i).row
"
></span>
</span>
class="cell"
(mouseenter)="setColRow(i)"
(mouseleave)="clearColRow()"
(click)="createTable(i)"
>
<span
class="square"
[class.active]="
selectedCell &&
selectedCell.column >= getCellColRow(i).column &&
selectedCell.row >= getCellColRow(i).row
"
></span>
</span>
}
</div>
</div>

View File

@@ -1,7 +1,7 @@
import { Component, inject } from '@angular/core';
import { ActivatedRoute, RouterModule } from '@angular/router';
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
import { CommonModule } from '@angular/common';
import type {
Cell,
PluginMessageEvent,
@@ -12,7 +12,7 @@ import { filter, fromEvent, map, merge, take } from 'rxjs';
import { FormBuilder, ReactiveFormsModule, FormGroup } from '@angular/forms';
@Component({
imports: [RouterModule, CommonModule, ReactiveFormsModule],
imports: [RouterModule, ReactiveFormsModule],
selector: 'app-root',
templateUrl: './app.component.html',
styleUrl: './app.component.css',

View File

@@ -1,7 +1,9 @@
import { provideZoneChangeDetection } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, appConfig).catch((err) =>
console.error(err),
);
bootstrapApplication(AppComponent, {
...appConfig,
providers: [provideZoneChangeDetection(), ...appConfig.providers],
}).catch((err) => console.error(err));

View File

@@ -2,7 +2,8 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": []
"types": [],
"moduleResolution": "bundler"
},
"files": ["src/main.ts"],
"include": ["src/**/*.d.ts"],

View File

@@ -8,7 +8,9 @@
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
"noFallthroughCasesInSwitch": true,
"moduleResolution": "bundler",
"module": "preserve"
},
"files": [],
"include": [],

View File

@@ -1,7 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"types": []
"types": [],
"moduleResolution": "bundler"
},
"files": ["src/plugin.ts"],
"include": ["../../libs/plugin-types/index.d.ts"]

View File

@@ -1 +0,0 @@
export default { extends: ['@commitlint/config-conventional'] };

View File

@@ -116,4 +116,7 @@ export default [
files: ['**/*.js', '**/*.jsx'],
rules: {},
},
{
ignores: ['**/vite.config.*.timestamp*', '**/vitest.config.*.timestamp*'],
},
];

View File

@@ -16,7 +16,7 @@ vi.mock('./create-sandbox.js', () => ({
describe('createPlugin', () => {
let mockContext: Context;
let manifest: Manifest;
let onCloseCallback: ReturnType<typeof vi.fn>;
let onCloseCallback: () => void;
let mockPluginManager: Awaited<ReturnType<typeof createPluginManager>>;
let mockSandbox: ReturnType<typeof createSandbox>;

View File

@@ -18,8 +18,8 @@ vi.mock('./api/openUI.api.js', () => ({
describe('createPluginManager', () => {
let mockContext: Context;
let manifest: Manifest;
let onCloseCallback: ReturnType<typeof vi.fn>;
let onReloadModal: ReturnType<typeof vi.fn>;
let onCloseCallback: () => void;
let onReloadModal: () => void;
let mockModal: {
setTheme: ReturnType<typeof vi.fn>;
remove: ReturnType<typeof vi.fn>;

View File

@@ -1,4 +1,5 @@
/// <reference types='vitest' />
/// <reference types="vitest/config" />
import { defineConfig } from 'vite';
import dts from 'vite-plugin-dts';
import { viteStaticCopy } from 'vite-plugin-static-copy';
@@ -14,8 +15,7 @@ export default defineConfig({
nxViteTsPaths(),
dts({
entryRoot: 'src',
tsConfigFilePath: path.join(__dirname, 'tsconfig.lib.json'),
skipDiagnostics: true,
tsconfigPath: path.join(__dirname, 'tsconfig.lib.json'),
}),
checker({
typescript: {

12
plugins/libs/ui/.babelrc Normal file
View File

@@ -0,0 +1,12 @@
{
"presets": [
[
"@nx/react/babel",
{
"runtime": "automatic",
"useBuiltIns": "usage"
}
]
],
"plugins": []
}

View File

@@ -0,0 +1,27 @@
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
stories: ['../src/lib/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: [],
framework: {
name: getAbsolutePath('@storybook/react-vite'),
options: {
builder: {
viteConfigPath: 'vite.config.mts',
},
},
},
};
function getAbsolutePath(value: string): any {
return dirname(fileURLToPath(import.meta.resolve(`${value}/package.json`)));
}
export default config;
// To customize your Vite configuration you can use the viteFinal field.
// Check https://storybook.js.org/docs/react/builders/vite#configuration
// and https://nx.dev/recipes/storybook/custom-builder-configs

View File

11
plugins/libs/ui/README.md Normal file
View File

@@ -0,0 +1,11 @@
# UI
A React component library with TypeScript for the Penpot ecosystem.
## Commands
Run from workspace root:
- **`pnpm storybook:ui`** - Start Storybook for component development
- **`pnpm build:ui`** - Build the library for production
- **`pnpm start:ui`** - Build in watch mode for development

View File

@@ -0,0 +1,39 @@
// @ts-check
import baseConfig, { compat } from '../../eslint.base.config.js';
import storybook from 'eslint-plugin-storybook';
import unusedImports from 'eslint-plugin-unused-imports';
import reactHooks from 'eslint-plugin-react-hooks';
export default [
...baseConfig,
reactHooks.configs.flat.recommended,
...storybook.configs['flat/recommended'],
...compat.config({ extends: ['plugin:@nx/react-typescript'] }).map((config) => ({
...config,
files: ['**/*.ts', '**/*.tsx'],
})),
{
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
ignores: ['**/.storybook/**'],
},
{
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
plugins: {
'unused-imports': unusedImports,
},
rules: {
'@typescript-eslint/no-unused-vars': 'off',
'unused-imports/no-unused-imports': 'error',
'unused-imports/no-unused-vars': [
'warn',
{
vars: 'all',
varsIgnorePattern: '^_',
args: 'after-used',
argsIgnorePattern: '^_',
},
],
},
},
];

View File

@@ -0,0 +1,39 @@
{
"name": "@penpot/ui",
"version": "0.0.1",
"types": "./index.d.ts",
"type": "module",
"exports": {
".": {
"import": "./index.js"
},
"./style.css": "./index.css"
},
"scripts": {
"start": "vite build --watch"
},
"devDependencies": {
"@babel/core": "^7.14.5",
"@babel/preset-react": "^7.14.5",
"@storybook/react": "10.2.0",
"@storybook/react-vite": "10.2.0",
"@testing-library/dom": "10.4.0",
"@testing-library/react": "16.3.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitejs/plugin-react": "^4.2.0",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-jsx-a11y": "6.10.1",
"eslint-plugin-react": "7.35.0",
"eslint-plugin-react-hooks": "7.0.1",
"storybook": "10.2.0"
},
"dependencies": {
"react": "^19.2.0",
"react-dom": "^19.2.0"
},
"peerDependencies": {
"react": ">=19.2",
"react-dom": ">=19.2"
}
}

View File

@@ -0,0 +1,8 @@
{
"name": "ui",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/ui/src",
"projectType": "library",
"tags": [],
"targets": {}
}

View File

@@ -0,0 +1 @@
export * from './lib/example/Example';

View File

@@ -0,0 +1,5 @@
.container {
background-color: #f0f0f0;
padding: 16px;
border: 2px solid #000;
}

View File

@@ -0,0 +1,10 @@
import { render } from '@testing-library/react';
import Example from './Example';
describe('Example', () => {
it('should render successfully', () => {
const { baseElement } = render(<Example />);
expect(baseElement).toBeTruthy();
});
});

View File

@@ -0,0 +1,12 @@
import { Example } from './Example';
import type { Meta, StoryObj } from '@storybook/react-vite';
const meta = {
title: 'UI/Example',
component: Example,
} satisfies Meta<typeof Example>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Primary: Story = {};

View File

@@ -0,0 +1,11 @@
import styles from './Example.module.css';
export function Example() {
return (
<div className={styles.container}>
<h1>Example!</h1>
</div>
);
}
export default Example;

View File

@@ -0,0 +1,33 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": false,
"noEmit": true,
"skipLibCheck": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"jsx": "react-jsx",
"types": ["vite/client", "vitest"],
"baseUrl": "."
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
},
{
"path": "./tsconfig.storybook.json"
}
]
}

View File

@@ -0,0 +1,39 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": [
"node",
"@nx/react/typings/cssmodule.d.ts",
"@nx/react/typings/image.d.ts",
"vite/client"
]
},
"exclude": [
"**/*.spec.ts",
"**/*.test.ts",
"**/*.spec.tsx",
"**/*.test.tsx",
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx",
"vite.config.ts",
"vite.config.mts",
"vitest.config.ts",
"vitest.config.mts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.test.tsx",
"src/**/*.spec.tsx",
"src/**/*.test.js",
"src/**/*.spec.js",
"src/**/*.test.jsx",
"src/**/*.spec.jsx",
"**/*.stories.ts",
"**/*.stories.js",
"**/*.stories.jsx",
"**/*.stories.tsx"
],
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"]
}

View File

@@ -0,0 +1,28 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": [
"vitest/globals",
"vitest/importMeta",
"vite/client",
"node",
"vitest"
]
},
"include": [
"vite.config.ts",
"vite.config.mts",
"vitest.config.ts",
"vitest.config.mts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.test.tsx",
"src/**/*.spec.tsx",
"src/**/*.test.js",
"src/**/*.spec.js",
"src/**/*.test.jsx",
"src/**/*.spec.jsx",
"src/**/*.d.ts"
]
}

View File

@@ -0,0 +1,33 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"emitDecoratorMetadata": true,
"outDir": "",
"module": "esnext",
"moduleResolution": "bundler"
},
"exclude": [
"src/**/*.spec.ts",
"src/**/*.test.ts",
"src/**/*.spec.js",
"src/**/*.test.js",
"src/**/*.spec.tsx",
"src/**/*.test.tsx",
"src/**/*.spec.jsx",
"src/**/*.test.js"
],
"include": [
"src/**/*.stories.ts",
"src/**/*.stories.js",
"src/**/*.stories.jsx",
"src/**/*.stories.tsx",
"src/**/*.stories.mdx",
".storybook/*.js",
".storybook/*.ts"
],
"files": [
"../../node_modules/@nx/react/typings/styled-jsx.d.ts",
"../../node_modules/@nx/react/typings/cssmodule.d.ts",
"../../node_modules/@nx/react/typings/image.d.ts"
]
}

View File

@@ -0,0 +1,71 @@
/// <reference types='vitest' />
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import dts from 'vite-plugin-dts';
import * as path from 'path';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
import { copyFileSync } from 'node:fs';
const copyCssPlugin = () => ({
name: 'copy-css',
closeBundle: () => {
try {
copyFileSync(
'dist/index.css',
'../../../frontend/resources/public/css/ui.css',
);
} catch (e) {
console.log('Error copying css file', e);
}
},
});
export default defineConfig(() => ({
root: import.meta.dirname,
cacheDir: '../../node_modules/.vite/libs/ui',
plugins: [
react({
// babel: {
// plugins: ['babel-plugin-react-compiler'],
// },
}),
nxViteTsPaths(),
nxCopyAssetsPlugin(['*.md']),
dts({
entryRoot: 'src',
tsconfigPath: path.join(import.meta.dirname, 'tsconfig.lib.json'),
pathsToAliases: false,
}),
copyCssPlugin(),
],
build: {
outDir: 'dist/',
emptyOutDir: true,
reportCompressedSize: true,
commonjsOptions: {
transformMixedEsModules: true,
},
lib: {
entry: 'src/index.ts',
name: 'ui',
fileName: 'index',
formats: ['es' as const],
},
rollupOptions: {
external: ['react', 'react-dom', 'react/jsx-runtime'],
},
},
test: {
name: 'ui',
watch: false,
globals: true,
environment: 'jsdom',
include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
reporters: ['default'],
coverage: {
reportsDirectory: '../../coverage/libs/ui',
provider: 'v8' as const,
},
},
}));

View File

@@ -1,12 +1,12 @@
{
"cli": {
"packageManager": "pnpm"
},
"pluginsConfig": {
"@nx/js": {
"analyzeSourceFiles": true
}
},
"cli": {
"packageManager": "pnpm"
},
"release": {
"projects": [
"libs/plugins-styles",
@@ -52,7 +52,6 @@
"options": {
"buildTargetName": "build",
"previewTargetName": "preview",
"testTargetName": "test",
"serveTargetName": "serve",
"serveStaticTargetName": "serve-static"
}
@@ -62,6 +61,23 @@
"options": {
"targetName": "lint"
}
},
{
"plugin": "@nx/vitest",
"options": {
"testTargetName": "test"
}
},
{
"plugin": "@nx/storybook/plugin",
"options": {
"serveStorybookTargetName": "storybook",
"buildStorybookTargetName": "build-storybook",
"testStorybookTargetName": "test-storybook",
"staticStorybookTargetName": "static-storybook",
"buildDepsTargetName": "build-deps",
"watchDepsTargetName": "watch-deps"
}
}
],
"generators": {
@@ -79,7 +95,67 @@
"style": "css",
"unitTestRunner": "none",
"projectNameAndRootFormat": "as-provided"
},
"@nx/angular:component": {
"type": "component"
},
"@schematics/angular:component": {
"type": "component"
},
"@nx/angular:directive": {
"type": "directive"
},
"@schematics/angular:directive": {
"type": "directive"
},
"@nx/angular:service": {
"type": "service"
},
"@schematics/angular:service": {
"type": "service"
},
"@nx/angular:scam": {
"type": "component"
},
"@nx/angular:scam-directive": {
"type": "directive"
},
"@nx/angular:guard": {
"typeSeparator": "."
},
"@schematics/angular:guard": {
"typeSeparator": "."
},
"@nx/angular:interceptor": {
"typeSeparator": "."
},
"@schematics/angular:interceptor": {
"typeSeparator": "."
},
"@nx/angular:module": {
"typeSeparator": "."
},
"@schematics/angular:module": {
"typeSeparator": "."
},
"@nx/angular:pipe": {
"typeSeparator": "."
},
"@schematics/angular:pipe": {
"typeSeparator": "."
},
"@nx/angular:resolver": {
"typeSeparator": "."
},
"@schematics/angular:resolver": {
"typeSeparator": "."
},
"@nx/react": {
"library": {
"unitTestRunner": "vitest"
}
}
},
"useLegacyCache": false
"useLegacyCache": false,
"nxCloudId": "69720151957fca05c743ec9a"
}

View File

@@ -3,11 +3,12 @@
"version": "0.6.0",
"type": "module",
"license": "MIT",
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6",
"scripts": {
"start": "npm run start:app:runtime",
"start": "pnpm run start:app:runtime",
"start:app:runtime": "concurrently --kill-others --names build,server \"nx run plugins-runtime:build --watch --mode development\" \"nx run plugins-runtime:preview\"",
"start:app:styles-example": "nx run example-styles:serve --host 0.0.0.0 --port 4201",
"start:plugin:all": "concurrently --kill-others \"npm:start:plugin:*(!all)\"",
"start:plugin:all": "concurrently --kill-others \"pnpm:start:plugin:*(!all)\"",
"start:plugin:poc-state": "nx run poc-state-plugin:init",
"start:plugin:contrast": "nx run contrast-plugin:init",
"start:plugin:icons": "nx run icons-plugin:init",
@@ -16,95 +17,104 @@
"start:plugin:table": "nx run table-plugin:init",
"start:plugin:renamelayers": "nx run rename-layers-plugin:init",
"start:plugin:colors-to-tokens": "nx run colors-to-tokens-plugin:init",
"start:ui": "nx run ui:start",
"build": "nx build plugins-runtime --emptyOutDir=true",
"build:plugins": "nx run-many -t build --parallel -p tag:type:plugin --exclude=poc-state-plugin",
"build:styles-example": "nx run example-styles:build",
"build:ui": "nx build ui --emptyOutDir=true",
"lint": "nx run-many --all --target=lint --parallel",
"format": "nx format:write --libs-and-apps",
"format:check": "nx format:check --libs-and-apps",
"lint:affected": "nx affected --target=lint",
"test": "nx run-many -t test --parallel -p plugins-runtime lorem-ipsum-plugin colors-to-tokens-plugin",
"test:e2e": "nx test e2e",
"storybook:ui": "pnpm nx storybook ui",
"registry": "nx local-registry",
"build:doc": "typedoc --tsconfig libs/plugins-runtime/tsconfig.lib.json --customCss ./tools/typedoc.css --out dist/doc",
"release": "tsx ./tools/scripts/publish.ts"
},
"private": true,
"devDependencies": {
"@angular-devkit/build-angular": "19.1.4",
"@angular-devkit/core": "19.1.4",
"@angular-devkit/schematics": "19.0.7",
"@angular-eslint/eslint-plugin": "19.0.2",
"@angular-eslint/eslint-plugin-template": "19.0.2",
"@angular-eslint/template-parser": "19.0.2",
"@angular/cli": "~19.0.0",
"@angular/compiler-cli": "19.1.3",
"@angular/language-service": "19.1.3",
"@commitlint/cli": "^18.6.0",
"@commitlint/config-conventional": "^18.6.0",
"@eslint/eslintrc": "^2.1.1",
"@eslint/js": "8.57.0",
"@nx/angular": "20.3.2",
"@nx/esbuild": "20.3.2",
"@nx/eslint": "20.3.2",
"@nx/eslint-plugin": "20.3.2",
"@nx/js": "20.3.2",
"@nx/node": "20.3.2",
"@nx/vite": "20.3.2",
"@nx/web": "20.3.2",
"@schematics/angular": "19.0.7",
"@swc-node/register": "1.9.2",
"@swc/core": "1.5.7",
"@swc/helpers": "0.5.12",
"@angular-devkit/build-angular": "21.1.1",
"@angular-devkit/core": "21.1.1",
"@angular-devkit/schematics": "21.1.1",
"@angular-eslint/eslint-plugin": "21.1.0",
"@angular-eslint/eslint-plugin-template": "21.1.0",
"@angular-eslint/template-parser": "21.1.0",
"@angular/cli": "21.1.1",
"@angular/compiler-cli": "21.1.1",
"@angular/language-service": "21.1.1",
"@eslint/eslintrc": "^3.3.3",
"@eslint/js": "9.39.2",
"@nx/angular": "22.4.0",
"@nx/devkit": "^22.4.0",
"@nx/esbuild": "22.4.0",
"@nx/eslint": "22.4.0",
"@nx/eslint-plugin": "22.4.0",
"@nx/react": "22.4.0",
"@nx/storybook": "22.4.0",
"@nx/js": "22.4.0",
"@nx/node": "22.4.0",
"@nx/vite": "22.4.0",
"@nx/vitest": "22.4.0",
"@nx/web": "22.4.0",
"@schematics/angular": "21.1.1",
"@swc-node/register": "1.11.1",
"@swc/core": "1.15.10",
"@swc/helpers": "0.5.18",
"@types/feather-icons": "^4.29.4",
"@types/node": "20.11.16",
"@types/uuid": "^9.0.8",
"@typescript-eslint/eslint-plugin": "7.18.0",
"@typescript-eslint/parser": "7.18.0",
"@typescript-eslint/utils": "^7.16.0",
"@vitest/coverage-v8": "1.6.0",
"@vitest/ui": "1.6.0",
"concurrently": "^8.2.2",
"esbuild": "^0.19.2",
"eslint": "8.57.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-deprecation": "^2.0.0",
"fs-extra": "^11.2.0",
"globals": "^15.1.0",
"happy-dom": "^13.6.2",
"husky": "^9.0.10",
"jsdom": "~22.1.0",
"jsonc-eslint-parser": "^2.1.0",
"nx": "20.3.2",
"prettier": "^3.4.2",
"swc-loader": "0.1.15",
"tsx": "^4.16.2",
"typedoc": "^0.26.5",
"typescript": "5.6.3",
"vite": "^5.0.0",
"vite-plugin-checker": "^0.6.4",
"vite-plugin-dts": "~2.3.0",
"vite-plugin-static-copy": "^3.1.0",
"vitest": "1.6.0"
"@types/node": "25.0.10",
"@typescript-eslint/eslint-plugin": "8.53.1",
"@typescript-eslint/parser": "8.53.1",
"@typescript-eslint/utils": "^8.53.1",
"@vitest/coverage-v8": "4.0.17",
"@vitest/ui": "4.0.17",
"babel-plugin-react-compiler": "^1.0.0",
"concurrently": "^9.2.1",
"esbuild": "^0.27.2",
"eslint": "9.39.2",
"eslint-config-prettier": "10.1.8",
"eslint-plugin-deprecation": "^3.0.0",
"eslint-plugin-storybook": "^10.2.0",
"eslint-plugin-unused-imports": "^4.3.0",
"fs-extra": "^11.3.3",
"globals": "^17.0.0",
"happy-dom": "^20.3.4",
"jiti": "2.6.1",
"jsdom": "~27.4.0",
"jsonc-eslint-parser": "^2.4.2",
"nx": "22.4.0",
"prettier": "^3.8.1",
"swc-loader": "0.2.7",
"ts-node": "10.9.1",
"tsx": "^4.21.0",
"typedoc": "^0.28.16",
"typescript": "5.9.3",
"typescript-eslint": "^8.53.1",
"vite": "7.3.1",
"vite-plugin-checker": "^0.12.0",
"vite-plugin-dts": "4.5.4",
"vite-plugin-static-copy": "^3.1.5",
"vitest": "4.0.17"
},
"dependencies": {
"@angular/animations": "19.1.3",
"@angular/common": "19.1.3",
"@angular/compiler": "19.1.3",
"@angular/core": "19.1.3",
"@angular/forms": "19.1.3",
"@angular/platform-browser": "19.1.3",
"@angular/platform-browser-dynamic": "19.1.3",
"@angular/router": "19.1.3",
"axios": "^1.6.0",
"@angular/animations": "21.1.1",
"@angular/common": "21.1.1",
"@angular/compiler": "21.1.1",
"@angular/core": "21.1.1",
"@angular/forms": "21.1.1",
"@angular/platform-browser": "21.1.1",
"@angular/platform-browser-dynamic": "21.1.1",
"@angular/router": "21.1.1",
"axios": "^1.13.2",
"feather-icons": "^4.29.2",
"puppeteer": "^22.11.0",
"rxjs": "~7.8.0",
"ses": "^1.13.1",
"tslib": "^2.3.0",
"uuid": "^9.0.1",
"zod": "^3.22.4",
"zone.js": "0.15.0"
"puppeteer": "^24.35.0",
"rxjs": "~7.8.2",
"ses": "^1.14.0",
"tslib": "^2.8.1",
"uuid": "^13.0.0",
"zod": "^4.3.5",
"zone.js": "0.16.0"
},
"nx": {
"includedScripts": []

25106
plugins/pnpm-lock.yaml generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,8 @@
"paths": {
"@penpot/plugin-types": ["libs/plugin-types/index.d.ts"],
"plugins-runtime": ["libs/plugins-runtime/src/index.ts"],
"plugins-styles/*": ["libs/plugins-styles/src/*"]
"plugins-styles/*": ["libs/plugins-styles/src/*"],
"ui": ["libs/ui/src/index.ts"]
}
},
"exclude": ["node_modules", "tmp"]