mirror of
https://github.com/penpot/penpot-plugins.git
synced 2025-01-21 06:02:34 -05:00
feat: support for plugin data
This commit is contained in:
parent
d90e7dbcf0
commit
fbee228760
5 changed files with 99 additions and 63 deletions
46
libs/plugin-types/index.d.ts
vendored
46
libs/plugin-types/index.d.ts
vendored
|
@ -1,8 +1,20 @@
|
|||
/**
|
||||
* TODO PenpotPluginData
|
||||
*/
|
||||
export interface PenpotPluginData {
|
||||
getPluginData(key: string): string;
|
||||
setPluginData(key: string, value: string): void;
|
||||
getPluginDataKeys(): string[];
|
||||
getSharedPluginData(namespace: string, key: string): string;
|
||||
setSharedPluginData(namespace: string, key: string, value: string): void;
|
||||
getSharedPluginDataKeys(namespace: string): string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* PenpotFile represents a file in the Penpot application.
|
||||
* It includes properties for the file's identifier, name, and revision number.
|
||||
*/
|
||||
export interface PenpotFile {
|
||||
export interface PenpotFile extends PenpotPluginData {
|
||||
id: string;
|
||||
name: string;
|
||||
revn: number;
|
||||
|
@ -12,7 +24,7 @@ export interface PenpotFile {
|
|||
* PenpotPage represents a page in the Penpot application.
|
||||
* It includes properties for the page's identifier and name, as well as methods for managing shapes on the page.
|
||||
*/
|
||||
export interface PenpotPage {
|
||||
export interface PenpotPage extends PenpotPluginData {
|
||||
/**
|
||||
* The `id` property is a unique identifier for the page.
|
||||
*/
|
||||
|
@ -465,7 +477,7 @@ interface PenpotPathCommand {
|
|||
/**
|
||||
* TODO PenpotShapeBase
|
||||
*/
|
||||
export interface PenpotShapeBase {
|
||||
export interface PenpotShapeBase extends PenpotPluginData {
|
||||
id: string;
|
||||
name: string;
|
||||
x: number;
|
||||
|
@ -635,6 +647,23 @@ export interface PenpotPath extends PenpotShapeBase {
|
|||
content: Array<PenpotPathCommand>;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO PenpotTextRange
|
||||
*/
|
||||
export interface PenpotTextRange {
|
||||
shape: PenpotText;
|
||||
|
||||
fontId: string | 'mixed';
|
||||
fontFamily: string | 'mixed';
|
||||
fontVariantId: string | 'mixed';
|
||||
fontSize: string | 'mixed';
|
||||
fontWeight: string | 'mixed';
|
||||
fontStyle: string | 'mixed';
|
||||
lineHeight: string | 'mixed';
|
||||
letterSpacing: string | 'mixed';
|
||||
textTransform: string | 'mixed';
|
||||
}
|
||||
|
||||
/**
|
||||
* PenpotText represents a text element in the Penpot application, extending the base shape interface.
|
||||
* It includes various properties to define the text content and its styling attributes.
|
||||
|
@ -653,6 +682,8 @@ export interface PenpotText extends PenpotShapeBase {
|
|||
lineHeight: string | 'mixed';
|
||||
letterSpacing: string | 'mixed';
|
||||
textTransform: string | 'mixed';
|
||||
|
||||
getRange(start: number, end: number): PenpotTextRange;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -760,7 +791,7 @@ export type PenpotTheme = 'light' | 'dark';
|
|||
/**
|
||||
* TODO PenpotLibraryElement
|
||||
*/
|
||||
export interface PenpotLibraryElement {
|
||||
export interface PenpotLibraryElement extends PenpotPluginData {
|
||||
readonly id: string;
|
||||
readonly libraryId: string;
|
||||
name: string;
|
||||
|
@ -831,7 +862,7 @@ export interface PenpotLibraryTypography extends PenpotLibraryElement {
|
|||
* applyToTextRange code
|
||||
* ```
|
||||
*/
|
||||
applyToTextRange(shape: PenpotShape): void;
|
||||
applyToTextRange(range: PenpotTextRange): void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -852,7 +883,8 @@ export interface PenpotLibraryComponent extends PenpotLibraryElement {
|
|||
/**
|
||||
* TODO PenpotLibrary
|
||||
*/
|
||||
export type PenpotLibrary = {
|
||||
|
||||
export interface PenpotLibrary extends PenpotPluginData {
|
||||
colors: PenpotLibraryColor[];
|
||||
typographies: PenpotLibraryTypography[];
|
||||
components: PenpotLibraryComponent[];
|
||||
|
@ -884,7 +916,7 @@ export type PenpotLibrary = {
|
|||
* ```
|
||||
*/
|
||||
createComponent(shapes: PenpotShape[]): PenpotLibraryComponent;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO PenpotLibraryContext
|
||||
|
|
|
@ -3,10 +3,10 @@ import './lib/modal/plugin-modal';
|
|||
|
||||
import {
|
||||
ɵloadPlugin,
|
||||
setContext,
|
||||
setContextBuilder,
|
||||
ɵloadPluginByUrl,
|
||||
} from './lib/load-plugin.js';
|
||||
import * as api from './lib/api/index.js';
|
||||
|
||||
import type { PenpotContext } from '@penpot/plugin-types';
|
||||
|
||||
console.log('%c[PLUGINS] Loading plugin system', 'color: #008d7c');
|
||||
|
@ -20,18 +20,12 @@ repairIntrinsics({
|
|||
|
||||
const globalThisAny$ = globalThis as any;
|
||||
|
||||
globalThisAny$.initPluginsRuntime = (context: PenpotContext) => {
|
||||
if (context) {
|
||||
console.log('%c[PLUGINS] Initialize context', 'color: #008d7c');
|
||||
|
||||
globalThisAny$.ɵcontext = context;
|
||||
globalThis.ɵloadPlugin = ɵloadPlugin;
|
||||
globalThis.ɵloadPluginByUrl = ɵloadPluginByUrl;
|
||||
|
||||
setContext(context);
|
||||
|
||||
for (const event of api.validEvents) {
|
||||
context.addListener(event, api.triggerEvent.bind(null, event));
|
||||
}
|
||||
}
|
||||
globalThisAny$.initPluginsRuntime = (
|
||||
contextBuilder: (id: string) => PenpotContext
|
||||
) => {
|
||||
console.log('%c[PLUGINS] Initialize runtime', 'color: #008d7c');
|
||||
setContextBuilder(contextBuilder);
|
||||
globalThisAny$.ɵcontext = contextBuilder('TEST');
|
||||
globalThis.ɵloadPlugin = ɵloadPlugin;
|
||||
globalThis.ɵloadPluginByUrl = ɵloadPluginByUrl;
|
||||
};
|
||||
|
|
|
@ -31,6 +31,7 @@ describe('Plugin api', () => {
|
|||
};
|
||||
|
||||
const api = createApi(mockContext as any, {
|
||||
pluginId: 'test',
|
||||
name: 'test',
|
||||
code: '',
|
||||
host: 'http://fake.com',
|
||||
|
|
|
@ -3,19 +3,32 @@ import type { PenpotContext } from '@penpot/plugin-types';
|
|||
import { createApi } from './api/index.js';
|
||||
import { loadManifest, loadManifestCode } from './parse-manifest.js';
|
||||
import { Manifest } from './models/manifest.model.js';
|
||||
import * as api from './api/index.js';
|
||||
|
||||
let isLockedDown = false;
|
||||
let createdApis: ReturnType<typeof createApi>[] = [];
|
||||
const multiPlugin = false;
|
||||
|
||||
let pluginContext: PenpotContext | null = null;
|
||||
export type ContextBuilder = (id: string) => PenpotContext;
|
||||
|
||||
export function setContext(context: PenpotContext) {
|
||||
pluginContext = context;
|
||||
let contextBuilder: ContextBuilder | null = null;
|
||||
|
||||
export function setContextBuilder(builder: ContextBuilder) {
|
||||
contextBuilder = builder;
|
||||
}
|
||||
|
||||
export const ɵloadPlugin = async function (manifest: Manifest) {
|
||||
try {
|
||||
const context = contextBuilder && contextBuilder(manifest.pluginId);
|
||||
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const event of api.validEvents) {
|
||||
context.addListener(event, api.triggerEvent.bind(null, event));
|
||||
}
|
||||
|
||||
const code = await loadManifestCode(manifest);
|
||||
|
||||
if (!isLockedDown) {
|
||||
|
@ -29,46 +42,42 @@ export const ɵloadPlugin = async function (manifest: Manifest) {
|
|||
});
|
||||
}
|
||||
|
||||
if (pluginContext) {
|
||||
const pluginApi = createApi(pluginContext, manifest);
|
||||
createdApis.push(pluginApi);
|
||||
const pluginApi = createApi(context, manifest);
|
||||
createdApis.push(pluginApi);
|
||||
|
||||
const c = new Compartment({
|
||||
penpot: harden(pluginApi),
|
||||
fetch: harden((...args: Parameters<typeof fetch>) => {
|
||||
const requestArgs: RequestInit = {
|
||||
...args[1],
|
||||
credentials: 'omit',
|
||||
};
|
||||
const c = new Compartment({
|
||||
penpot: harden(pluginApi),
|
||||
fetch: harden((...args: Parameters<typeof fetch>) => {
|
||||
const requestArgs: RequestInit = {
|
||||
...args[1],
|
||||
credentials: 'omit',
|
||||
};
|
||||
|
||||
return fetch(args[0], requestArgs);
|
||||
}),
|
||||
console: harden(window.console),
|
||||
Math: harden(Math),
|
||||
setTimeout: harden(
|
||||
(...[handler, timeout]: Parameters<typeof setTimeout>) => {
|
||||
return setTimeout(() => {
|
||||
handler();
|
||||
}, timeout);
|
||||
}
|
||||
),
|
||||
clearTimeout: harden((id: Parameters<typeof clearTimeout>[0]) => {
|
||||
clearTimeout(id);
|
||||
}),
|
||||
return fetch(args[0], requestArgs);
|
||||
}),
|
||||
console: harden(window.console),
|
||||
Math: harden(Math),
|
||||
setTimeout: harden(
|
||||
(...[handler, timeout]: Parameters<typeof setTimeout>) => {
|
||||
return setTimeout(() => {
|
||||
handler();
|
||||
}, timeout);
|
||||
}
|
||||
),
|
||||
clearTimeout: harden((id: Parameters<typeof clearTimeout>[0]) => {
|
||||
clearTimeout(id);
|
||||
}),
|
||||
});
|
||||
|
||||
c.evaluate(code);
|
||||
|
||||
const listenerId: symbol = context.addListener('finish', () => {
|
||||
createdApis.forEach((pluginApi) => {
|
||||
pluginApi.closePlugin();
|
||||
});
|
||||
|
||||
c.evaluate(code);
|
||||
|
||||
const listenerId: symbol = pluginContext.addListener('finish', () => {
|
||||
createdApis.forEach((pluginApi) => {
|
||||
pluginApi.closePlugin();
|
||||
});
|
||||
|
||||
pluginContext?.removeListener(listenerId);
|
||||
});
|
||||
} else {
|
||||
console.error('Cannot find Penpot Context');
|
||||
}
|
||||
context?.removeListener(listenerId);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
@ -76,6 +85,5 @@ export const ɵloadPlugin = async function (manifest: Manifest) {
|
|||
|
||||
export const ɵloadPluginByUrl = async function (manifestUrl: string) {
|
||||
const manifest = await loadManifest(manifestUrl);
|
||||
|
||||
ɵloadPlugin(manifest);
|
||||
};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
export const manifestSchema = z.object({
|
||||
pluginId: z.string(),
|
||||
name: z.string(),
|
||||
host: z.string().url(),
|
||||
code: z.string(),
|
||||
|
|
Loading…
Add table
Reference in a new issue