mirror of
https://github.com/penpot/penpot-plugins.git
synced 2025-01-22 14:49:27 -05:00
feat: lorem ipsum plugin
This commit is contained in:
parent
7283a9bd36
commit
ec729e2e59
28 changed files with 823 additions and 73 deletions
11
README.md
11
README.md
|
@ -64,11 +64,12 @@ Open in your browser: `http://localhost:4210/`
|
||||||
|
|
||||||
## Sample plugins
|
## Sample plugins
|
||||||
|
|
||||||
| Plugin | Description | PORT | Start command | Manifest URL |
|
| Plugin | Description | PORT | Start command | Manifest URL |
|
||||||
| ---------------- | ----------------------------------------------------------- | ---- | ----------------------------- | ------------------------------------------ |
|
| ------------------ | ----------------------------------------------------------- | ---- | ------------------------------- | ------------------------------------------ |
|
||||||
| poc-state-plugin | Sandbox plugin to test new plugins api functionality | 4301 | npm run start:pc-plugin | http://localhost:4301/assets/manifest.json |
|
| poc-state-plugin | Sandbox plugin to test new plugins api functionality | 4301 | npm run start:pc-plugin | http://localhost:4301/assets/manifest.json |
|
||||||
| contrast-plugin | Sample plugin that gives you color contrast information | 4302 | npm run start:contrast-plugin | http://localhost:4302/manifest.json |
|
| contrast-plugin | Sample plugin that gives you color contrast information | 4302 | npm run start:contrast-plugin | http://localhost:4302/manifest.json |
|
||||||
| icons-plugin | Tool to add icons from [Feather](https://feathericons.com/) | 4303 | npm run start:icons-plugin | http://localhost:4303/assets/manifest.json |
|
| icons-plugin | Tool to add icons from [Feather](https://feathericons.com/) | 4303 | npm run start:icons-plugin | http://localhost:4303/assets/manifest.json |
|
||||||
|
| lorem-ipsum-plugin | Generate Lorem ipsum text | 4304 | npm run start:loremipsum-plugin | http://localhost:4304/assets/manifest.json |
|
||||||
|
|
||||||
## Web Apps
|
## Web Apps
|
||||||
|
|
||||||
|
|
43
apps/lorem-ipsum-plugin/eslint.config.js
Normal file
43
apps/lorem-ipsum-plugin/eslint.config.js
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import baseConfig from '../../eslint.config.js';
|
||||||
|
import { compat } from '../../eslint.base.config.js';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...baseConfig,
|
||||||
|
...compat
|
||||||
|
.config({
|
||||||
|
extends: [
|
||||||
|
'plugin:@nx/angular',
|
||||||
|
'plugin:@angular-eslint/template/process-inline-templates',
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.map((config) => ({
|
||||||
|
...config,
|
||||||
|
files: ['**/*.ts'],
|
||||||
|
rules: {
|
||||||
|
'@angular-eslint/directive-selector': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
type: 'attribute',
|
||||||
|
prefix: 'app',
|
||||||
|
style: 'camelCase',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'@angular-eslint/component-selector': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
prefix: 'app',
|
||||||
|
style: 'kebab-case',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
...compat
|
||||||
|
.config({ extends: ['plugin:@nx/angular-template'] })
|
||||||
|
.map((config) => ({
|
||||||
|
...config,
|
||||||
|
files: ['**/*.html'],
|
||||||
|
rules: {},
|
||||||
|
})),
|
||||||
|
{ ignores: ['**/assets/*.js'] },
|
||||||
|
];
|
93
apps/lorem-ipsum-plugin/project.json
Normal file
93
apps/lorem-ipsum-plugin/project.json
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
{
|
||||||
|
"name": "lorem-ipsum-plugin",
|
||||||
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||||
|
"projectType": "application",
|
||||||
|
"prefix": "app",
|
||||||
|
"sourceRoot": "apps/lorem-ipsum-plugin/src",
|
||||||
|
"tags": ["type:plugin"],
|
||||||
|
"targets": {
|
||||||
|
"build": {
|
||||||
|
"executor": "@angular-devkit/build-angular:application",
|
||||||
|
"outputs": ["{options.outputPath}"],
|
||||||
|
"options": {
|
||||||
|
"outputPath": "dist/apps/lorem-ipsum-plugin",
|
||||||
|
"index": "apps/lorem-ipsum-plugin/src/index.html",
|
||||||
|
"browser": "apps/lorem-ipsum-plugin/src/main.ts",
|
||||||
|
"polyfills": ["zone.js"],
|
||||||
|
"tsConfig": "apps/lorem-ipsum-plugin/tsconfig.app.json",
|
||||||
|
"assets": [
|
||||||
|
"apps/lorem-ipsum-plugin/src/favicon.ico",
|
||||||
|
"apps/lorem-ipsum-plugin/src/assets"
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"libs/plugins-styles/src/lib/styles.css",
|
||||||
|
"apps/lorem-ipsum-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"
|
||||||
|
},
|
||||||
|
"serve": {
|
||||||
|
"executor": "@angular-devkit/build-angular:dev-server",
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"buildTarget": "lorem-ipsum-plugin:build:production"
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"buildTarget": "lorem-ipsum-plugin:build:development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "development"
|
||||||
|
},
|
||||||
|
"extract-i18n": {
|
||||||
|
"executor": "@angular-devkit/build-angular:extract-i18n",
|
||||||
|
"options": {
|
||||||
|
"buildTarget": "lorem-ipsum-plugin:build"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"buildPlugin": {
|
||||||
|
"executor": "@nx/esbuild:esbuild",
|
||||||
|
"outputs": [
|
||||||
|
"{options.outputPath}"
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"minify": true,
|
||||||
|
"outputPath": "apps/lorem-ipsum-plugin/src/assets/",
|
||||||
|
"main": "apps/lorem-ipsum-plugin/src/plugin.ts",
|
||||||
|
"tsConfig": "apps/lorem-ipsum-plugin/tsconfig.plugin.json",
|
||||||
|
"generatePackageJson": false,
|
||||||
|
"format": [
|
||||||
|
"esm"
|
||||||
|
],
|
||||||
|
"deleteOutputPath": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
47
apps/lorem-ipsum-plugin/src/app/app.component.css
Normal file
47
apps/lorem-ipsum-plugin/src/app/app.component.css
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
p {
|
||||||
|
color: var(--df-secondary);
|
||||||
|
margin-block-end: var(--spacing-16);
|
||||||
|
}
|
||||||
|
|
||||||
|
.generation-options {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--spacing-8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.generation-size {
|
||||||
|
inline-size: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generation-type {
|
||||||
|
inline-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sections-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
padding-block-start: var(--spacing-24);
|
||||||
|
|
||||||
|
button {
|
||||||
|
inline-size: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.regular-generate {
|
||||||
|
padding-block-end: var(--spacing-24);
|
||||||
|
border-block-end: 2px solid var(--background-quaternary);
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-block-start: var(--spacing-12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.extra-options {
|
||||||
|
margin-block-start: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--spacing-20);
|
||||||
|
}
|
118
apps/lorem-ipsum-plugin/src/app/app.component.ts
Normal file
118
apps/lorem-ipsum-plugin/src/app/app.component.ts
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
import { Component, inject } from '@angular/core';
|
||||||
|
import { toSignal } from '@angular/core/rxjs-interop';
|
||||||
|
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import type {
|
||||||
|
GenerationTypes,
|
||||||
|
PluginMessageEvent,
|
||||||
|
PluginUIEvent,
|
||||||
|
} from '../model';
|
||||||
|
import { filter, fromEvent, map, merge, take } from 'rxjs';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
imports: [ReactiveFormsModule],
|
||||||
|
selector: 'app-root',
|
||||||
|
template: `
|
||||||
|
<form [formGroup]="form" class="sections-wrapper" (ngSubmit)="generate()">
|
||||||
|
<section class="regular-generate">
|
||||||
|
<p class="body-s">
|
||||||
|
Select a text field to replace it with a placeholder text.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="generation-options">
|
||||||
|
<input
|
||||||
|
formControlName="num"
|
||||||
|
type="number"
|
||||||
|
class="input generation-size"
|
||||||
|
min="1"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<select formControlName="type" class="select generation-type">
|
||||||
|
<option value="paragraphs">Paragraphs</option>
|
||||||
|
<option value="sentences">Sentences</option>
|
||||||
|
<option value="words">Words</option>
|
||||||
|
<option value="characters">Characters</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="submit" data-appearance="secondary">Generate</button>
|
||||||
|
</section>
|
||||||
|
<section class="extra-options">
|
||||||
|
<div class="checkbox-container">
|
||||||
|
<input
|
||||||
|
formControlName="startWith"
|
||||||
|
class="checkbox-input"
|
||||||
|
type="checkbox"
|
||||||
|
id="startWith"
|
||||||
|
value="checkbox_second"
|
||||||
|
/>
|
||||||
|
<label for="startWith" class="body-s">Start with 'Lorem Ipsum'</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="checkbox-container">
|
||||||
|
<input
|
||||||
|
formControlName="autoClose"
|
||||||
|
class="checkbox-input"
|
||||||
|
type="checkbox"
|
||||||
|
id="autoClose"
|
||||||
|
value="checkbox_second"
|
||||||
|
/>
|
||||||
|
<label for="autoClose" class="body-s">Auto close</label>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
`,
|
||||||
|
styleUrl: './app.component.css',
|
||||||
|
host: {
|
||||||
|
'[attr.data-theme]': 'theme()',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export class AppComponent {
|
||||||
|
route = inject(ActivatedRoute);
|
||||||
|
messages$ = fromEvent<MessageEvent<PluginMessageEvent>>(window, 'message');
|
||||||
|
|
||||||
|
initialTheme$ = this.route.queryParamMap.pipe(
|
||||||
|
map((params) => params.get('theme')),
|
||||||
|
filter((theme) => !!theme),
|
||||||
|
take(1)
|
||||||
|
);
|
||||||
|
|
||||||
|
theme = toSignal(
|
||||||
|
merge(
|
||||||
|
this.initialTheme$,
|
||||||
|
this.messages$.pipe(
|
||||||
|
filter((event) => event.data.type === 'theme'),
|
||||||
|
map((event) => {
|
||||||
|
return event.data.content;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
form = new FormGroup({
|
||||||
|
num: new FormControl<number>(1, { nonNullable: true }),
|
||||||
|
type: new FormControl<GenerationTypes>('paragraphs', { nonNullable: true }),
|
||||||
|
startWith: new FormControl(true, { nonNullable: true }),
|
||||||
|
autoClose: new FormControl(true, { nonNullable: true }),
|
||||||
|
});
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.#sendMessage({ type: 'ready' });
|
||||||
|
}
|
||||||
|
|
||||||
|
generate() {
|
||||||
|
const formValue = this.form.getRawValue();
|
||||||
|
|
||||||
|
this.#sendMessage({
|
||||||
|
type: 'text',
|
||||||
|
generationType: formValue.type,
|
||||||
|
startWithLorem: formValue.startWith,
|
||||||
|
size: formValue.num,
|
||||||
|
autoClose: formValue.autoClose,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#sendMessage(message: PluginUIEvent) {
|
||||||
|
parent.postMessage(message, '*');
|
||||||
|
}
|
||||||
|
}
|
6
apps/lorem-ipsum-plugin/src/app/app.config.ts
Normal file
6
apps/lorem-ipsum-plugin/src/app/app.config.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { ApplicationConfig } from '@angular/core';
|
||||||
|
import { provideRouter } from '@angular/router';
|
||||||
|
|
||||||
|
export const appConfig: ApplicationConfig = {
|
||||||
|
providers: [provideRouter([])],
|
||||||
|
};
|
7
apps/lorem-ipsum-plugin/src/assets/manifest.json
Normal file
7
apps/lorem-ipsum-plugin/src/assets/manifest.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"name": "Lorem ipsum",
|
||||||
|
"host": "http://localhost:4304",
|
||||||
|
"description": "Lorem ipsum text generator plugin",
|
||||||
|
"code": "/assets/plugin.js",
|
||||||
|
"permissions": ["page:read", "file:read", "selection:read"]
|
||||||
|
}
|
69
apps/lorem-ipsum-plugin/src/generator.spec.ts
Normal file
69
apps/lorem-ipsum-plugin/src/generator.spec.ts
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import {
|
||||||
|
generateCharacters,
|
||||||
|
generateWords,
|
||||||
|
generateSentences,
|
||||||
|
generateParagraphs,
|
||||||
|
} from './generator';
|
||||||
|
|
||||||
|
describe('generateCharacters', () => {
|
||||||
|
it('should generate the correct number of characters starting with "Lorem ipsum"', () => {
|
||||||
|
const result = generateCharacters(20);
|
||||||
|
expect(result.length).toBe(20);
|
||||||
|
expect(result.startsWith('Lorem ipsum')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate the correct number of characters without starting with "Lorem ipsum"', () => {
|
||||||
|
const result = generateCharacters(40, false);
|
||||||
|
expect(result.length).toBe(40);
|
||||||
|
expect(result.startsWith('Lorem ipsum')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('generateWords', () => {
|
||||||
|
it('should generate the correct number of words starting with "Lorem ipsum"', () => {
|
||||||
|
const result = generateWords(5);
|
||||||
|
const words = result.split(' ');
|
||||||
|
expect(words.length).toBe(5);
|
||||||
|
expect(result.startsWith('Lorem ipsum')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate the correct number of words without starting with "Lorem ipsum"', () => {
|
||||||
|
const result = generateWords(10, false);
|
||||||
|
const words = result.split(' ');
|
||||||
|
expect(words.length).toBe(10);
|
||||||
|
expect(result.startsWith('Lorem ipsum')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('generateSentences', () => {
|
||||||
|
it('should generate the correct number of sentences starting with "Lorem ipsum"', () => {
|
||||||
|
const result = generateSentences(3);
|
||||||
|
const sentences = result.split('. ');
|
||||||
|
expect(sentences.length).toBe(3);
|
||||||
|
expect(result.startsWith('Lorem ipsum')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate the correct number of sentences without starting with "Lorem ipsum"', () => {
|
||||||
|
const result = generateSentences(6, false);
|
||||||
|
const sentences = result.split('. ');
|
||||||
|
expect(sentences.length).toBe(6);
|
||||||
|
expect(result.startsWith('Lorem ipsum')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('generateParagraphs', () => {
|
||||||
|
it('should generate the correct number of paragraphs starting with "Lorem ipsum"', () => {
|
||||||
|
const result = generateParagraphs(2);
|
||||||
|
const paragraphs = result.split('\n\n');
|
||||||
|
expect(paragraphs.length).toBe(2);
|
||||||
|
expect(result.startsWith('Lorem ipsum')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate the correct number of paragraphs without starting with "Lorem ipsum"', () => {
|
||||||
|
const result = generateParagraphs(4, false);
|
||||||
|
const paragraphs = result.split('\n\n');
|
||||||
|
expect(paragraphs.length).toBe(4);
|
||||||
|
expect(result.startsWith('Lorem ipsum')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
142
apps/lorem-ipsum-plugin/src/generator.ts
Normal file
142
apps/lorem-ipsum-plugin/src/generator.ts
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
const wordList = [
|
||||||
|
'dolor',
|
||||||
|
'sit',
|
||||||
|
'amet',
|
||||||
|
'consectetur',
|
||||||
|
'adipiscing',
|
||||||
|
'elit',
|
||||||
|
'sed',
|
||||||
|
'do',
|
||||||
|
'eiusmod',
|
||||||
|
'tempor',
|
||||||
|
'incididunt',
|
||||||
|
'labore',
|
||||||
|
'et',
|
||||||
|
'dolore',
|
||||||
|
'magna',
|
||||||
|
'aliqua',
|
||||||
|
'enim',
|
||||||
|
'ad',
|
||||||
|
'minim',
|
||||||
|
'veniam',
|
||||||
|
'quis',
|
||||||
|
'nostrud',
|
||||||
|
'exercitation',
|
||||||
|
'ullamco',
|
||||||
|
'laboris',
|
||||||
|
'nisi',
|
||||||
|
'ut',
|
||||||
|
'aliquip',
|
||||||
|
'ex',
|
||||||
|
'ea',
|
||||||
|
'commodo',
|
||||||
|
'consequat',
|
||||||
|
'duis',
|
||||||
|
'aute',
|
||||||
|
'irure',
|
||||||
|
'in',
|
||||||
|
'reprehenderit',
|
||||||
|
'voluptate',
|
||||||
|
'velit',
|
||||||
|
'esse',
|
||||||
|
'cillum',
|
||||||
|
'eu',
|
||||||
|
'fugiat',
|
||||||
|
'nulla',
|
||||||
|
'pariatur',
|
||||||
|
'excepteur',
|
||||||
|
'sint',
|
||||||
|
'occaecat',
|
||||||
|
'cupidatat',
|
||||||
|
'non',
|
||||||
|
'proident',
|
||||||
|
'sunt',
|
||||||
|
'culpa',
|
||||||
|
'qui',
|
||||||
|
'officia',
|
||||||
|
'deserunt',
|
||||||
|
'mollit',
|
||||||
|
'anim',
|
||||||
|
'id',
|
||||||
|
'est',
|
||||||
|
'laborum',
|
||||||
|
];
|
||||||
|
|
||||||
|
const lorem = 'Lorem ipsum' as const;
|
||||||
|
|
||||||
|
function* randomWordGenerator() {
|
||||||
|
let copyWordList: string[] = [];
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (!copyWordList.length) {
|
||||||
|
copyWordList = [...wordList];
|
||||||
|
}
|
||||||
|
|
||||||
|
const newWordIndex = Math.floor(Math.random() * copyWordList.length);
|
||||||
|
|
||||||
|
yield copyWordList[newWordIndex];
|
||||||
|
|
||||||
|
copyWordList.splice(newWordIndex, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRandomWordGenerator = randomWordGenerator();
|
||||||
|
|
||||||
|
function getRandomWord() {
|
||||||
|
return getRandomWordGenerator.next().value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateCharacters(count: number, startWithLorem = true) {
|
||||||
|
let text = '';
|
||||||
|
|
||||||
|
if (startWithLorem) {
|
||||||
|
text = lorem + ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
while (text.length < count) {
|
||||||
|
text += getRandomWord() + ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
return text.slice(0, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateWords(count: number, startWithLorem = true) {
|
||||||
|
let words = [];
|
||||||
|
|
||||||
|
if (startWithLorem) {
|
||||||
|
words.push(...lorem.split(' ').slice(0, count));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = words.length; i < count; i++) {
|
||||||
|
words.push(getRandomWord());
|
||||||
|
}
|
||||||
|
|
||||||
|
return words.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateSentences(count: number, startWithLorem = true) {
|
||||||
|
let sentences = [];
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
let sentenceLength = Math.floor(Math.random() * 10) + 3; // between 3 and 12 words per sentence
|
||||||
|
let sentence = generateWords(sentenceLength, false);
|
||||||
|
|
||||||
|
if (startWithLorem && i === 0) {
|
||||||
|
sentence =
|
||||||
|
lorem + ' ' + sentence.charAt(0).toLowerCase() + sentence.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
sentences.push(sentence.charAt(0).toUpperCase() + sentence.slice(1) + '.');
|
||||||
|
}
|
||||||
|
return sentences.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateParagraphs(count: number, startWithLorem = true) {
|
||||||
|
let paragraphs = [];
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
let paragraphLength = Math.floor(Math.random() * 5) + 3; // between 3 and 7 sentences per paragraph
|
||||||
|
paragraphs.push(
|
||||||
|
generateSentences(paragraphLength, startWithLorem && i === 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return paragraphs.join('\n\n');
|
||||||
|
}
|
12
apps/lorem-ipsum-plugin/src/index.html
Normal file
12
apps/lorem-ipsum-plugin/src/index.html
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>lorem-ipsum-plugin</title>
|
||||||
|
<base href="/" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<app-root></app-root>
|
||||||
|
</body>
|
||||||
|
</html>
|
7
apps/lorem-ipsum-plugin/src/main.ts
Normal file
7
apps/lorem-ipsum-plugin/src/main.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
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)
|
||||||
|
);
|
40
apps/lorem-ipsum-plugin/src/model.ts
Normal file
40
apps/lorem-ipsum-plugin/src/model.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
export type GenerationTypes =
|
||||||
|
| 'paragraphs'
|
||||||
|
| 'sentences'
|
||||||
|
| 'words'
|
||||||
|
| 'characters';
|
||||||
|
|
||||||
|
export interface InitPluginUIEvent {
|
||||||
|
type: 'ready';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TextPluginUIEvent {
|
||||||
|
type: 'text';
|
||||||
|
generationType: GenerationTypes;
|
||||||
|
startWithLorem: boolean;
|
||||||
|
size: number;
|
||||||
|
autoClose: boolean;
|
||||||
|
}
|
||||||
|
export type PluginUIEvent = InitPluginUIEvent | TextPluginUIEvent;
|
||||||
|
|
||||||
|
export interface InitPluginEvent {
|
||||||
|
type: 'init';
|
||||||
|
content: {
|
||||||
|
theme: string;
|
||||||
|
selection: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export interface SelectionPluginEvent {
|
||||||
|
type: 'selection';
|
||||||
|
content: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ThemePluginEvent {
|
||||||
|
type: 'theme';
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PluginMessageEvent =
|
||||||
|
| InitPluginEvent
|
||||||
|
| SelectionPluginEvent
|
||||||
|
| ThemePluginEvent;
|
70
apps/lorem-ipsum-plugin/src/plugin.ts
Normal file
70
apps/lorem-ipsum-plugin/src/plugin.ts
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import { PenpotText } from '@penpot/plugin-types';
|
||||||
|
import type {
|
||||||
|
PluginMessageEvent,
|
||||||
|
PluginUIEvent,
|
||||||
|
TextPluginUIEvent,
|
||||||
|
} from './model.js';
|
||||||
|
import {
|
||||||
|
generateParagraphs,
|
||||||
|
generateSentences,
|
||||||
|
generateWords,
|
||||||
|
generateCharacters,
|
||||||
|
} from './generator.js';
|
||||||
|
|
||||||
|
penpot.ui.open('LOREM IPSUM PLUGIN', `?theme=${penpot.getTheme()}`);
|
||||||
|
|
||||||
|
penpot.on('themechange', (theme) => {
|
||||||
|
sendMessage({ type: 'theme', content: theme });
|
||||||
|
});
|
||||||
|
|
||||||
|
function getSelectedShapes(): PenpotText[] {
|
||||||
|
return penpot.selection.filter((it): it is PenpotText => {
|
||||||
|
return penpot.utils.types.isText(it);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
penpot.on('selectionchange', () => {
|
||||||
|
sendMessage({ type: 'selection', content: getSelectedShapes().length });
|
||||||
|
});
|
||||||
|
|
||||||
|
penpot.ui.onMessage<PluginUIEvent>((message) => {
|
||||||
|
if (message.type === 'text') {
|
||||||
|
generateText(message);
|
||||||
|
|
||||||
|
if (message.autoClose) {
|
||||||
|
penpot.closePlugin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function sendMessage(message: PluginMessageEvent) {
|
||||||
|
penpot.ui.sendMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateText(event: TextPluginUIEvent) {
|
||||||
|
const selection = getSelectedShapes();
|
||||||
|
|
||||||
|
if (!selection.length) {
|
||||||
|
const text = penpot.createText('');
|
||||||
|
text.x = penpot.viewport.center.x;
|
||||||
|
text.y = penpot.viewport.center.y;
|
||||||
|
selection.push(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
selection.forEach((it) => {
|
||||||
|
switch (event.generationType) {
|
||||||
|
case 'paragraphs':
|
||||||
|
it.characters = generateParagraphs(event.size, event.startWithLorem);
|
||||||
|
break;
|
||||||
|
case 'sentences':
|
||||||
|
it.characters = generateSentences(event.size, event.startWithLorem);
|
||||||
|
break;
|
||||||
|
case 'words':
|
||||||
|
it.characters = generateWords(event.size, event.startWithLorem);
|
||||||
|
break;
|
||||||
|
case 'characters':
|
||||||
|
it.characters = generateCharacters(event.size, event.startWithLorem);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
0
apps/lorem-ipsum-plugin/src/styles.css
Normal file
0
apps/lorem-ipsum-plugin/src/styles.css
Normal file
10
apps/lorem-ipsum-plugin/tsconfig.app.json
Normal file
10
apps/lorem-ipsum-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": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"]
|
||||||
|
}
|
7
apps/lorem-ipsum-plugin/tsconfig.editor.json
Normal file
7
apps/lorem-ipsum-plugin/tsconfig.editor.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"include": ["src/**/*.ts"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": []
|
||||||
|
}
|
||||||
|
}
|
33
apps/lorem-ipsum-plugin/tsconfig.json
Normal file
33
apps/lorem-ipsum-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/lorem-ipsum-plugin/tsconfig.plugin.json
Normal file
8
apps/lorem-ipsum-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"]
|
||||||
|
}
|
20
apps/lorem-ipsum-plugin/vite.config.ts
Normal file
20
apps/lorem-ipsum-plugin/vite.config.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/// <reference types='vitest' />
|
||||||
|
import { defineConfig } from 'vite';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
root: __dirname,
|
||||||
|
cacheDir: '../node_modules/.vite/lorem-ipsum-plugin',
|
||||||
|
test: {
|
||||||
|
globals: true,
|
||||||
|
cache: {
|
||||||
|
dir: '../node_modules/.vitest',
|
||||||
|
},
|
||||||
|
environment: 'jsdom',
|
||||||
|
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||||
|
reporters: ['default'],
|
||||||
|
coverage: {
|
||||||
|
reportsDirectory: '../coverage/lorem-ipsum-plugin',
|
||||||
|
provider: 'v8',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -7,7 +7,7 @@ This guide walks you through the steps to create an Angular plugin.
|
||||||
First, you need to create the scaffolding for your plugin. Use the following command, replacing `example-plugin` with the name of your 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
|
```sh
|
||||||
npx nx g @nx/angular:app example-plugin --directory=apps/example-plugin
|
npx nx g @nx/angular:app example-plugin --directory=apps/example-plugin --bundler=esbuild
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 2: Configure the Manifest
|
### Step 2: Configure the Manifest
|
||||||
|
@ -17,7 +17,7 @@ Next, create a `manifest.json` file inside the `/src/assets` directory. This fil
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"name": "Example plugin",
|
"name": "Example plugin",
|
||||||
"code": "http://localhost:4202/assets/plugin.js",
|
"code": "http://localhost:4200/assets/plugin.js",
|
||||||
"permissions": ["page:read", "file:read", "selection:read"]
|
"permissions": ["page:read", "file:read", "selection:read"]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -27,6 +27,7 @@ Next, create a `manifest.json` file inside the `/src/assets` directory. This fil
|
||||||
Now, add the following configuration to your `project.json` to compile the `plugin.ts` file:
|
Now, add the following configuration to your `project.json` to compile the `plugin.ts` file:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
|
"tags": ["type:plugin"],
|
||||||
"targets": {
|
"targets": {
|
||||||
"buildPlugin": {
|
"buildPlugin": {
|
||||||
"executor": "@nx/esbuild:esbuild",
|
"executor": "@nx/esbuild:esbuild",
|
||||||
|
@ -87,12 +88,20 @@ Add the reference to the main tsconfig.json:
|
||||||
],
|
],
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 5: Run the plugin
|
### Strep 5: Hello world plugin code
|
||||||
|
|
||||||
|
Create the file `apps/example-plugin/src/plugin.ts` with the following code:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
console.log('Hello Plugin');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 6: Run the plugin
|
||||||
|
|
||||||
Run this command:
|
Run this command:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npx nx run-many --targets=buildPlugin,serve --projects=poc-state-plugin --watch
|
npx nx run-many --targets=buildPlugin,serve --projects=example-plugin --watch
|
||||||
```
|
```
|
||||||
|
|
||||||
This will run two tasks: `serve`, the usual Angular server, and `buildPlugin`, which will compile the `plugin.ts` file.
|
This will run two tasks: `serve`, the usual Angular server, and `buildPlugin`, which will compile the `plugin.ts` file.
|
||||||
|
|
2
libs/plugin-types/index.d.ts
vendored
2
libs/plugin-types/index.d.ts
vendored
|
@ -460,7 +460,7 @@ export interface Penpot
|
||||||
open: (
|
open: (
|
||||||
name: string,
|
name: string,
|
||||||
url: string,
|
url: string,
|
||||||
options: { width: number; height: number }
|
options?: { width: number; height: number }
|
||||||
) => void;
|
) => void;
|
||||||
/**
|
/**
|
||||||
* Description of sendMessage
|
* Description of sendMessage
|
||||||
|
|
|
@ -71,7 +71,7 @@ export function createApi(context: PenpotContext, manifest: Manifest): Penpot {
|
||||||
|
|
||||||
const penpot: Penpot = {
|
const penpot: Penpot = {
|
||||||
ui: {
|
ui: {
|
||||||
open: (name: string, url: string, options: OpenUIOptions) => {
|
open: (name: string, url: string, options?: OpenUIOptions) => {
|
||||||
const theme = context.getTheme() as 'light' | 'dark';
|
const theme = context.getTheme() as 'light' | 'dark';
|
||||||
|
|
||||||
modal = openUIApi(
|
modal = openUIApi(
|
||||||
|
|
|
@ -4,7 +4,12 @@ import { createModal } from '../create-modal.js';
|
||||||
|
|
||||||
export default z
|
export default z
|
||||||
.function()
|
.function()
|
||||||
.args(z.string(), z.string(), z.enum(['dark', 'light']), openUISchema)
|
.args(
|
||||||
|
z.string(),
|
||||||
|
z.string(),
|
||||||
|
z.enum(['dark', 'light']),
|
||||||
|
openUISchema.optional()
|
||||||
|
)
|
||||||
.implement((title, url, theme, options) => {
|
.implement((title, url, theme, options) => {
|
||||||
return createModal(title, url, theme, options);
|
return createModal(title, url, theme, options);
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,7 +6,7 @@ export function createModal(
|
||||||
name: string,
|
name: string,
|
||||||
url: string,
|
url: string,
|
||||||
theme: PenpotTheme,
|
theme: PenpotTheme,
|
||||||
options: OpenUIOptions
|
options?: OpenUIOptions
|
||||||
) {
|
) {
|
||||||
const modal = document.createElement('plugin-modal') as PluginModalElement;
|
const modal = document.createElement('plugin-modal') as PluginModalElement;
|
||||||
|
|
||||||
|
@ -14,8 +14,8 @@ export function createModal(
|
||||||
|
|
||||||
modal.setAttribute('title', name);
|
modal.setAttribute('title', name);
|
||||||
modal.setAttribute('iframe-src', url);
|
modal.setAttribute('iframe-src', url);
|
||||||
modal.setAttribute('width', String(options.width || 285));
|
modal.setAttribute('width', String(options?.width || 285));
|
||||||
modal.setAttribute('height', String(options.height || 540));
|
modal.setAttribute('height', String(options?.height || 540));
|
||||||
|
|
||||||
document.body.appendChild(modal);
|
document.body.appendChild(modal);
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ export const ɵloadPlugin = async function (manifest: Manifest) {
|
||||||
penpot: harden(lastApi),
|
penpot: harden(lastApi),
|
||||||
fetch: window.fetch.bind(window),
|
fetch: window.fetch.bind(window),
|
||||||
console: harden(window.console),
|
console: harden(window.console),
|
||||||
|
Math: harden(Math),
|
||||||
});
|
});
|
||||||
|
|
||||||
c.evaluate(code);
|
c.evaluate(code);
|
||||||
|
|
|
@ -43,7 +43,6 @@
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
border-block-end: 2px solid var(--color-background-quaternary);
|
border-block-end: 2px solid var(--color-background-quaternary);
|
||||||
padding-block-end: var(--spacing-4);
|
padding-block-end: var(--spacing-4);
|
||||||
margin-block-end: var(--spacing-20);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
|
|
|
@ -1,98 +1,100 @@
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Work+Sans:wght@400+500&display=swap');
|
@import url('https://fonts.googleapis.com/css?family=Work+Sans:wght@400+500&display=swap');
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
/* Font weight */
|
/* Font weight */
|
||||||
--font-weight-regular: 400;
|
--font-weight-regular: 400;
|
||||||
--font-weight-bold: 500;
|
--font-weight-bold: 500;
|
||||||
--font-line-height-s: 1.2;
|
--font-line-height-s: 1.2;
|
||||||
--font-line-height-m: 1.4;
|
--font-line-height-m: 1.4;
|
||||||
--font-line-height-l: 1.5;
|
--font-line-height-l: 1.5;
|
||||||
--font-size-s: 12px;
|
--font-size-s: 12px;
|
||||||
--font-size-m: 14px;
|
--font-size-m: 14px;
|
||||||
--font-size-l: 16px;
|
--font-size-l: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body {
|
html,
|
||||||
font-family: 'Work Sans', sans-serif;
|
body {
|
||||||
font-optical-sizing: auto;
|
font-family: 'Work Sans', sans-serif;
|
||||||
font-style: normal;
|
font-optical-sizing: auto;
|
||||||
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
font-family: 'Work Sans', sans-serif;
|
font-family: 'Work Sans', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.display {
|
.display {
|
||||||
font-weight: var(--font-weight-regular);
|
font-weight: var(--font-weight-regular);
|
||||||
font-size: 36px;
|
font-size: 36px;
|
||||||
line-height: var(--font-line-height-s);
|
line-height: var(--font-line-height-s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-s {
|
.title-s {
|
||||||
font-weight: var(--font-weight-regular);
|
font-weight: var(--font-weight-regular);
|
||||||
font-size: var(--font-size-m);
|
font-size: var(--font-size-m);
|
||||||
line-height: var(--font-line-height-s);
|
line-height: var(--font-line-height-s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-m {
|
.title-m {
|
||||||
font-weight: var(--font-weight-regular);
|
font-weight: var(--font-weight-regular);
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
line-height: var(--font-line-height-s);
|
line-height: var(--font-line-height-s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-l {
|
.title-l {
|
||||||
font-weight: var(--font-weight-regular);
|
font-weight: var(--font-weight-regular);
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.headline-s {
|
.headline-s {
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
font-size: var(--font-size-s);
|
font-size: var(--font-size-s);
|
||||||
line-height: var(--font-line-height-s);
|
line-height: var(--font-line-height-s);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
.headline-m {
|
.headline-m {
|
||||||
font-weight: var(--font-weight-regular);
|
font-weight: var(--font-weight-regular);
|
||||||
font-size: var(--font-size-l);
|
font-size: var(--font-size-l);
|
||||||
line-height: var(--font-line-height-m);
|
line-height: var(--font-line-height-m);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
.headline-l {
|
.headline-l {
|
||||||
font-weight: var(--font-weight-regular);
|
font-weight: var(--font-weight-regular);
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
line-height: var(--font-line-height-s);
|
line-height: var(--font-line-height-s);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
.body-s {
|
.body-s {
|
||||||
font-weight: var(--font-weight-regular);
|
font-weight: var(--font-weight-regular);
|
||||||
font-size: var(--font-size-s);
|
font-size: var(--font-size-s);
|
||||||
line-height: var(--font-line-height-m);
|
line-height: var(--font-line-height-m);
|
||||||
}
|
}
|
||||||
|
|
||||||
.body-m {
|
.body-m {
|
||||||
font-weight: var(--font-weight-regular);
|
font-weight: var(--font-weight-regular);
|
||||||
font-size: var(--font-size-m);
|
font-size: var(--font-size-m);
|
||||||
line-height: var(--font-line-height-l);
|
line-height: var(--font-line-height-l);
|
||||||
}
|
}
|
||||||
|
|
||||||
.body-l {
|
.body-l {
|
||||||
font-weight: var(--font-weight-regular);
|
font-weight: var(--font-weight-regular);
|
||||||
font-size: var(--font-size-l);
|
font-size: var(--font-size-l);
|
||||||
line-height: var(--font-line-height-l);
|
line-height: var(--font-line-height-l);
|
||||||
}
|
}
|
||||||
|
|
||||||
.caption {
|
.caption {
|
||||||
font-weight: var(--font-weight-regular);
|
font-weight: var(--font-weight-regular);
|
||||||
font-size: var(--font-size-s);
|
font-size: var(--font-size-s);
|
||||||
line-height: var(--font-line-height-s);
|
line-height: var(--font-line-height-s);
|
||||||
}
|
}
|
||||||
|
|
||||||
code, .code-font {
|
code,
|
||||||
font-weight: var(--font-weight-regular);
|
.code-font {
|
||||||
font-size: var(--font-size-s);
|
font-weight: var(--font-weight-regular);
|
||||||
line-height: var(--font-line-height-l);
|
font-size: var(--font-size-s);
|
||||||
}
|
line-height: var(--font-line-height-l);
|
||||||
|
}
|
||||||
|
|
|
@ -10,10 +10,11 @@
|
||||||
"start:rpc-api": "npx nx serve rpc-api",
|
"start:rpc-api": "npx nx serve rpc-api",
|
||||||
"start:styles-example": "npx nx run example-styles:serve --host 0.0.0.0 --port 4201",
|
"start:styles-example": "npx nx run example-styles:serve --host 0.0.0.0 --port 4201",
|
||||||
"start:icons-plugin": "npx nx run-many --targets=buildPlugin,serve --projects=icons-plugin --watch --host 0.0.0.0 --port 4303",
|
"start:icons-plugin": "npx nx run-many --targets=buildPlugin,serve --projects=icons-plugin --watch --host 0.0.0.0 --port 4303",
|
||||||
|
"start:loremipsum-plugin": "npx nx run-many --targets=buildPlugin,serve --projects=lorem-ipsum-plugin --watch --port 4304",
|
||||||
"build": "npx nx build plugins-runtime --emptyOutDir=true",
|
"build": "npx nx build plugins-runtime --emptyOutDir=true",
|
||||||
"lint": "nx run-many --all --target=lint --parallel",
|
"lint": "nx run-many --all --target=lint --parallel",
|
||||||
"lint:affected": "npx nx affected --target=lint",
|
"lint:affected": "npx nx affected --target=lint",
|
||||||
"test": "npx nx test plugins-runtime",
|
"test": "nx run-many -t test -p plugins-runtime lorem-ipsum-plugin",
|
||||||
"publish": "nx run-many -t publish -p plugins-styles plugin-types --parallel=false --",
|
"publish": "nx run-many -t publish -p plugins-styles plugin-types --parallel=false --",
|
||||||
"registry": "nx local-registry",
|
"registry": "nx local-registry",
|
||||||
"prepare": "husky",
|
"prepare": "husky",
|
||||||
|
|
Loading…
Add table
Reference in a new issue