diff --git a/libs/plugin-types/index.d.ts b/libs/plugin-types/index.d.ts index d9295a2..6e9277a 100644 --- a/libs/plugin-types/index.d.ts +++ b/libs/plugin-types/index.d.ts @@ -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; } +/** + * 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 diff --git a/libs/plugins-runtime/src/index.ts b/libs/plugins-runtime/src/index.ts index 3db4f9a..9cfc703 100644 --- a/libs/plugins-runtime/src/index.ts +++ b/libs/plugins-runtime/src/index.ts @@ -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; }; diff --git a/libs/plugins-runtime/src/lib/api/plugin-api.spec.ts b/libs/plugins-runtime/src/lib/api/plugin-api.spec.ts index 85c2a3b..9c07f3a 100644 --- a/libs/plugins-runtime/src/lib/api/plugin-api.spec.ts +++ b/libs/plugins-runtime/src/lib/api/plugin-api.spec.ts @@ -31,6 +31,7 @@ describe('Plugin api', () => { }; const api = createApi(mockContext as any, { + pluginId: 'test', name: 'test', code: '', host: 'http://fake.com', diff --git a/libs/plugins-runtime/src/lib/load-plugin.ts b/libs/plugins-runtime/src/lib/load-plugin.ts index 305ad43..1604823 100644 --- a/libs/plugins-runtime/src/lib/load-plugin.ts +++ b/libs/plugins-runtime/src/lib/load-plugin.ts @@ -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[] = []; 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) => { - const requestArgs: RequestInit = { - ...args[1], - credentials: 'omit', - }; + const c = new Compartment({ + penpot: harden(pluginApi), + fetch: harden((...args: Parameters) => { + const requestArgs: RequestInit = { + ...args[1], + credentials: 'omit', + }; - return fetch(args[0], requestArgs); - }), - console: harden(window.console), - Math: harden(Math), - setTimeout: harden( - (...[handler, timeout]: Parameters) => { - return setTimeout(() => { - handler(); - }, timeout); - } - ), - clearTimeout: harden((id: Parameters[0]) => { - clearTimeout(id); - }), + return fetch(args[0], requestArgs); + }), + console: harden(window.console), + Math: harden(Math), + setTimeout: harden( + (...[handler, timeout]: Parameters) => { + return setTimeout(() => { + handler(); + }, timeout); + } + ), + clearTimeout: harden((id: Parameters[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); }; diff --git a/libs/plugins-runtime/src/lib/models/manifest.schema.ts b/libs/plugins-runtime/src/lib/models/manifest.schema.ts index 2478781..a58e1bd 100644 --- a/libs/plugins-runtime/src/lib/models/manifest.schema.ts +++ b/libs/plugins-runtime/src/lib/models/manifest.schema.ts @@ -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(),