diff --git a/apps/poc-state-read-plugin/src/plugin.ts b/apps/poc-state-read-plugin/src/plugin.ts index d542c65..185c522 100644 --- a/apps/poc-state-read-plugin/src/plugin.ts +++ b/apps/poc-state-read-plugin/src/plugin.ts @@ -26,30 +26,40 @@ penpot.ui.onMessage<{ content: string; data: unknown }>((message) => { }, }); } else if (message.content === 'change-name') { - const shape = penpot.getPage()?.getShapeById('' + (message.data as {id: string}).id); + const shape = penpot + .getPage() + ?.getShapeById('' + (message.data as { id: string }).id); if (shape) { - shape.name = (message.data as {name: string}).name; + shape.name = (message.data as { name: string }).name; } } else if (message.content === 'create-rect') { const shape = penpot.createRectangle(); penpot.log(shape); } else if (message.content === 'move-x') { - const shape = penpot.getPage()?.getShapeById('' + (message.data as {id: string}).id); + const shape = penpot + .getPage() + ?.getShapeById('' + (message.data as { id: string }).id); if (shape) { shape.x += 100; } } else if (message.content === 'move-y') { - const shape = penpot.getPage()?.getShapeById('' + (message.data as {id: string}).id); + const shape = penpot + .getPage() + ?.getShapeById('' + (message.data as { id: string }).id); if (shape) { shape.y += 100; } } else if (message.content === 'resize-w') { - const shape = penpot.getPage()?.getShapeById('' + (message.data as {id: string}).id); + const shape = penpot + .getPage() + ?.getShapeById('' + (message.data as { id: string }).id); if (shape) { shape.resize(shape.width * 2, shape.height); } } else if (message.content === 'resize-h') { - const shape = penpot.getPage()?.getShapeById('' + (message.data as {id: string}).id); + const shape = penpot + .getPage() + ?.getShapeById('' + (message.data as { id: string }).id); if (shape) { shape.resize(shape.width, shape.height * 2); } @@ -57,15 +67,17 @@ penpot.ui.onMessage<{ content: string; data: unknown }>((message) => { const selection = penpot.selection; for (const shape of selection) { - shape.characters = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam id mauris ut felis finibus congue. Ut odio ipsum, condimentum id tellus sit amet, dapibus sagittis ligula. Pellentesque hendrerit, nulla sit amet aliquet scelerisque, orci nunc commodo tellus, quis hendrerit nisl massa non tellus. + if (penpot.utils.types.isText(shape)) { + shape.characters = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam id mauris ut felis finibus congue. Ut odio ipsum, condimentum id tellus sit amet, dapibus sagittis ligula. Pellentesque hendrerit, nulla sit amet aliquet scelerisque, orci nunc commodo tellus, quis hendrerit nisl massa non tellus. Phasellus fringilla tortor elit, ac dictum tellus posuere sodales. Ut eget imperdiet ante. Nunc eros magna, tincidunt non finibus in, tempor elementum nunc. Sed commodo magna in arcu aliquam efficitur.`; + } } } }); penpot.on('pagechange', () => { - const page = penpot.getPage(); + const page = penpot.getPage(); const shapes = page?.findShapes(); penpot.ui.sendMessage({ @@ -79,7 +91,7 @@ penpot.on('filechange', () => { penpot.ui.sendMessage({ type: 'file', content: { - id: file.id + id: file.id, }, }); }); diff --git a/libs/plugin-types/index.d.ts b/libs/plugin-types/index.d.ts index f9ee353..6174639 100644 --- a/libs/plugin-types/index.d.ts +++ b/libs/plugin-types/index.d.ts @@ -11,22 +11,67 @@ export interface PenpotPage { findShapes(): PenpotShape[]; } +export type PenpotGradient = { + type: 'linear' | 'radial'; + startX: number; + startY: number; + endX: number; + endY: number; + width: number; + stops: Array<{ color: string; opacity?: number; offset: number }>; +}; + +export type PenpotImageData = { + name?: string; + width: number; + height: number; + mtype?: string; + id: string; + keepApectRatio?: boolean; +}; + export interface PenpotFill { - fillColor: string; - fillOpacity: number; + fillColor?: string; + fillOpacity?: number; + fillColorGradient?: PenpotGradient; + fillColorRefFile?: string; + fillColorRefId?: string; + fillImage?: PenpotImageData; } -export interface PenpotShape { +export type PenpotStrokeCap = + | 'round' + | 'square' + | 'line-arrow' + | 'triangle-arrow' + | 'square-marker' + | 'circle-marker' + | 'diamond-marker'; + +export interface PenpotStroke { + strokeColor?: string; + strokeColorRefFile?: string; + strokeColorRefId?: string; + strokeOpacity?: number; + strokeStyle?: 'solid' | 'dotted' | 'dashed' | 'mixed' | 'none' | 'svg'; + strokeWidth?: number; + strokeAlignment?: 'center' | 'inner' | 'outer'; + strokeCapStart?: PenpotStrokeCap; + strokeCapEnd?: PenpotStrokeCap; + strokeColorGradient: PenpotGradient; +} + +export interface PenpotShapeBase { id: string; name: string; - type: 'frame' | 'group' | 'bool' | 'rect' | 'path' | 'text' | 'circle' | 'svg-raw' | 'image'; x: number; y: number; width: number; height: number; - children: PenpotShape[]; + fills: PenpotFill[]; strokes: PenpotStroke[]; + resize(width: number, height: number); } @@ -35,6 +80,71 @@ export interface PenpotText extends PenpotShape { characters: string; } +export interface PenpotFrame extends PenpotShapeBase { + readonly type: 'frame'; + readonly children: PenpotShape[]; +} + +export interface PenpotGroup extends PenpotShapeBase { + readonly type: 'group'; + readonly children: PenpotShape[]; +} + +export interface PenpotBool extends PenpotShapeBase { + readonly type: 'bool'; + readonly children: PenpotShape[]; +} + +export interface PenpotRectangle extends PenpotShapeBase { + readonly type: 'rect'; +} + +export interface PenpotPath extends PenpotShapeBase { + readonly type: 'rect'; +} + +export interface PenpotText extends PenpotShapeBase { + readonly type: 'text'; + characters: string; +} + +export interface PenpotCircle extends PenpotShapeBase { + type: 'circle'; +} + +export interface PenpotSvgRaw extends PenpotShapeBase { + type: 'svg-raw'; +} + +export interface PenpotImage extends PenpotShapeBase { + type: 'image'; +} + +export type PenpotPoint = { x: number; y: number }; +export type PenpotBounds = { + x: number; + y: number; + width: number; + height: number; +}; + +export interface PenpotViewport { + center: PenpotPoint; + zoom: number; + readonly bounds: PenpotBounds; +} + +export type PenpotShape = + | PenpotFrame + | PenpotGroup + | PenpotBool + | PenpotRectangle + | PenpotPath + | PenpotText + | PenpotCircle + | PenpotSvgRaw + | PenpotImage; + export interface EventsMap { pagechange: PenpotPage; filechange: PenpotFile; @@ -48,20 +158,19 @@ export interface PenpotContext { root: PenpotShape; currentPage: PenpotPage; selection: PenpotShape[]; + viewport: PenpotViewport; - // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents getFile(): PenpotFile | null; - - // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents getPage(): PenpotPage | null; getSelected(): string[]; getSelectedShapes(): PenpotShape[]; getTheme(): PenpotTheme; - createRectangle(): PenpotShape; + createRectangle(): PenpotRectangle; + createFrame(): PepotFrame; } -export interface Penpot { +export interface Penpot extends PenpotContext { ui: { open: ( name: string, @@ -71,6 +180,11 @@ export interface Penpot { sendMessage: (message: unknown) => void; onMessage: (callback: (message: T) => void) => void; }; + utils: { + types: { + isText(shape: PenpotShape): shape is PenpotText; + }; + }; log: (...data: unknown[]) => void; setTimeout: (callback: () => void, time: number) => void; closePlugin: () => void; diff --git a/libs/plugins-runtime/src/lib/api/index.ts b/libs/plugins-runtime/src/lib/api/index.ts index b77c3f6..e53fe6a 100644 --- a/libs/plugins-runtime/src/lib/api/index.ts +++ b/libs/plugins-runtime/src/lib/api/index.ts @@ -1,4 +1,10 @@ -import type { PenpotContext, Penpot, EventsMap, PenpotPage, PenpotShape } from '@penpot/plugin-types'; +import type { + PenpotContext, + Penpot, + EventsMap, + PenpotPage, + PenpotShape, +} from '@penpot/plugin-types'; import { Manifest, Permissions } from '../models/manifest.model'; import { OpenUIOptions } from '../models/open-ui-options.model'; @@ -80,6 +86,14 @@ export function createApi(context: PenpotContext, manifest: Manifest) { }, }, + utils: { + types: { + isText(shape: PenpotShape): shape is PenpotText { + return shape.type === 'text'; + }, + }, + }, + log: console.log, setTimeout: z @@ -143,7 +157,7 @@ export function createApi(context: PenpotContext, manifest: Manifest) { checkPermission('selection:read'); return context.selection; }, - + getFile(): PenpotFile { checkPermission('file:read'); return context.getFile(); @@ -168,7 +182,7 @@ export function createApi(context: PenpotContext, manifest: Manifest) { return context.getTheme(); }, - createRectangle(): PenpotShape { + createRectangle(): PenpotRectangle { // checkPermission('page:write'); return context.createRectangle(); }, @@ -178,4 +192,3 @@ export function createApi(context: PenpotContext, manifest: Manifest) { return penpot; } -