mirror of
https://github.com/penpot/penpot-mcp.git
synced 2026-04-25 11:18:37 +00:00
Apply formatter
This commit is contained in:
parent
f99fedb4f1
commit
4b755e4381
@ -39,17 +39,11 @@ The `manifest.json` file contains the basic information about the plugin. It def
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"name": "Your plugin name",
|
"name": "Your plugin name",
|
||||||
"description": "Your plugin description",
|
"description": "Your plugin description",
|
||||||
"code": "plugin-file.js",
|
"code": "plugin-file.js",
|
||||||
"icon": "your-icon.png",
|
"icon": "your-icon.png",
|
||||||
"permissions": [
|
"permissions": ["content:read", "content:write", "library:read", "library:write", "user:read"]
|
||||||
"content:read",
|
|
||||||
"content:write",
|
|
||||||
"library:read",
|
|
||||||
"library:write",
|
|
||||||
"user:read"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -1,33 +1,25 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Penpot plugin example</title>
|
<title>Penpot plugin example</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<p>Penpot plugin starter template</p>
|
<p>Penpot plugin starter template</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Checkout the
|
Checkout the
|
||||||
<a target="_blank" href="https://help.penpot.app/plugins/"
|
<a target="_blank" href="https://help.penpot.app/plugins/">documentation</a>
|
||||||
>documentation</a
|
to get started.
|
||||||
>
|
</p>
|
||||||
to get started.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<button type="button" data-appearance="primary" data-handler="create-text">
|
<button type="button" data-appearance="primary" data-handler="create-text">Create text</button>
|
||||||
Create text
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button type="button" data-appearance="secondary" data-handler="connect-mcp">
|
<button type="button" data-appearance="secondary" data-handler="connect-mcp">Connect to MCP server</button>
|
||||||
Connect to MCP server
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div id="connection-status" style="margin-top: 10px; font-size: 12px; color: #666;">
|
<div id="connection-status" style="margin-top: 10px; font-size: 12px; color: #666">Not connected</div>
|
||||||
Not connected
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
2350
penpot-plugin/package-lock.json
generated
2350
penpot-plugin/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,22 +1,22 @@
|
|||||||
{
|
{
|
||||||
"name": "penpot-plugin-starter-template",
|
"name": "penpot-plugin-starter-template",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite build --watch",
|
"dev": "vite build --watch",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"format:check": "prettier --check ."
|
"format:check": "prettier --check ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@penpot/plugin-styles": "1.3.2",
|
"@penpot/plugin-styles": "1.3.2",
|
||||||
"@penpot/plugin-types": "1.3.2"
|
"@penpot/plugin-types": "1.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prettier": "^3.0.0",
|
"prettier": "^3.0.0",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vite": "^7.0.5",
|
"vite": "^7.0.5",
|
||||||
"vite-live-preview": "^0.3.2"
|
"vite-live-preview": "^0.3.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Penpot MCP Plugin",
|
"name": "Penpot MCP Plugin",
|
||||||
"code": "plugin.js",
|
"code": "plugin.js",
|
||||||
"description": "This plugin enables interaction with the Penpot MCP server",
|
"description": "This plugin enables interaction with the Penpot MCP server",
|
||||||
"permissions": ["content:read", "content:write"]
|
"permissions": ["content:read", "content:write"]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,71 +12,70 @@ const statusElement = document.getElementById("connection-status");
|
|||||||
* Updates the connection status display element.
|
* Updates the connection status display element.
|
||||||
*/
|
*/
|
||||||
function updateConnectionStatus(status: string, isConnectedState: boolean): void {
|
function updateConnectionStatus(status: string, isConnectedState: boolean): void {
|
||||||
if (statusElement) {
|
if (statusElement) {
|
||||||
statusElement.textContent = status;
|
statusElement.textContent = status;
|
||||||
statusElement.style.color = isConnectedState ? "#4CAF50" : "#666";
|
statusElement.style.color = isConnectedState ? "#4CAF50" : "#666";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Establishes a WebSocket connection to the MCP server.
|
* Establishes a WebSocket connection to the MCP server.
|
||||||
*/
|
*/
|
||||||
function connectToMcpServer(): void {
|
function connectToMcpServer(): void {
|
||||||
if (ws?.readyState === WebSocket.OPEN) {
|
if (ws?.readyState === WebSocket.OPEN) {
|
||||||
updateConnectionStatus("Already connected", true);
|
updateConnectionStatus("Already connected", true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ws = new WebSocket("ws://localhost:8080");
|
ws = new WebSocket("ws://localhost:8080");
|
||||||
updateConnectionStatus("Connecting...", false);
|
updateConnectionStatus("Connecting...", false);
|
||||||
|
|
||||||
ws.onopen = () => {
|
ws.onopen = () => {
|
||||||
console.log("Connected to MCP server");
|
console.log("Connected to MCP server");
|
||||||
updateConnectionStatus("Connected to MCP server", true);
|
updateConnectionStatus("Connected to MCP server", true);
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onmessage = (event) => {
|
ws.onmessage = (event) => {
|
||||||
console.log("Received from MCP server:", event.data);
|
console.log("Received from MCP server:", event.data);
|
||||||
try {
|
try {
|
||||||
const message = JSON.parse(event.data);
|
const message = JSON.parse(event.data);
|
||||||
// Forward the task to the plugin for execution
|
// Forward the task to the plugin for execution
|
||||||
parent.postMessage(message, "*");
|
parent.postMessage(message, "*");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to parse WebSocket message:", error);
|
console.error("Failed to parse WebSocket message:", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onclose = () => {
|
ws.onclose = () => {
|
||||||
console.log("Disconnected from MCP server");
|
console.log("Disconnected from MCP server");
|
||||||
updateConnectionStatus("Disconnected", false);
|
updateConnectionStatus("Disconnected", false);
|
||||||
ws = null;
|
ws = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onerror = (error) => {
|
ws.onerror = (error) => {
|
||||||
console.error("WebSocket error:", error);
|
console.error("WebSocket error:", error);
|
||||||
updateConnectionStatus("Connection error", false);
|
updateConnectionStatus("Connection error", false);
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to connect to MCP server:", error);
|
console.error("Failed to connect to MCP server:", error);
|
||||||
updateConnectionStatus("Connection failed", false);
|
updateConnectionStatus("Connection failed", false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Event handlers
|
// Event handlers
|
||||||
document.querySelector("[data-handler='create-text']")?.addEventListener("click", () => {
|
document.querySelector("[data-handler='create-text']")?.addEventListener("click", () => {
|
||||||
// send message to plugin.ts
|
// send message to plugin.ts
|
||||||
parent.postMessage("create-text", "*");
|
parent.postMessage("create-text", "*");
|
||||||
});
|
});
|
||||||
|
|
||||||
document.querySelector("[data-handler='connect-mcp']")?.addEventListener("click", () => {
|
document.querySelector("[data-handler='connect-mcp']")?.addEventListener("click", () => {
|
||||||
connectToMcpServer();
|
connectToMcpServer();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen plugin.ts messages
|
// Listen plugin.ts messages
|
||||||
window.addEventListener("message", (event) => {
|
window.addEventListener("message", (event) => {
|
||||||
if (event.data.source === "penpot") {
|
if (event.data.source === "penpot") {
|
||||||
document.body.dataset.theme = event.data.theme;
|
document.body.dataset.theme = event.data.theme;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -2,25 +2,25 @@ penpot.ui.open("Penpot MCP Plugin", `?theme=${penpot.theme}`);
|
|||||||
|
|
||||||
// Handle both legacy string messages and new task-based messages
|
// Handle both legacy string messages and new task-based messages
|
||||||
penpot.ui.onMessage<string | { task: string; params: any }>((message) => {
|
penpot.ui.onMessage<string | { task: string; params: any }>((message) => {
|
||||||
// Legacy string-based message handling
|
// Legacy string-based message handling
|
||||||
if (typeof message === "string") {
|
if (typeof message === "string") {
|
||||||
if (message === "create-text") {
|
if (message === "create-text") {
|
||||||
const text = penpot.createText("Hello world!");
|
const text = penpot.createText("Hello world!");
|
||||||
|
|
||||||
if (text) {
|
if (text) {
|
||||||
text.x = penpot.viewport.center.x;
|
text.x = penpot.viewport.center.x;
|
||||||
text.y = penpot.viewport.center.y;
|
text.y = penpot.viewport.center.y;
|
||||||
|
|
||||||
penpot.selection = [text];
|
penpot.selection = [text];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// New task-based message handling
|
// New task-based message handling
|
||||||
if (typeof message === "object" && message.task) {
|
if (typeof message === "object" && message.task) {
|
||||||
handlePluginTask(message);
|
handlePluginTask(message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -29,16 +29,16 @@ penpot.ui.onMessage<string | { task: string; params: any }>((message) => {
|
|||||||
* @param taskMessage - The task message containing task type and parameters
|
* @param taskMessage - The task message containing task type and parameters
|
||||||
*/
|
*/
|
||||||
function handlePluginTask(taskMessage: { task: string; params: any }): void {
|
function handlePluginTask(taskMessage: { task: string; params: any }): void {
|
||||||
console.log("Executing plugin task:", taskMessage.task, taskMessage.params);
|
console.log("Executing plugin task:", taskMessage.task, taskMessage.params);
|
||||||
|
|
||||||
switch (taskMessage.task) {
|
switch (taskMessage.task) {
|
||||||
case "printText":
|
case "printText":
|
||||||
handlePrintTextTask(taskMessage.params);
|
handlePrintTextTask(taskMessage.params);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.warn("Unknown plugin task:", taskMessage.task);
|
console.warn("Unknown plugin task:", taskMessage.task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,36 +47,36 @@ function handlePluginTask(taskMessage: { task: string; params: any }): void {
|
|||||||
* @param params - The parameters containing the text to create
|
* @param params - The parameters containing the text to create
|
||||||
*/
|
*/
|
||||||
function handlePrintTextTask(params: { text: string }): void {
|
function handlePrintTextTask(params: { text: string }): void {
|
||||||
if (!params.text) {
|
if (!params.text) {
|
||||||
console.error("printText task requires 'text' parameter");
|
console.error("printText task requires 'text' parameter");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const text = penpot.createText(params.text);
|
const text = penpot.createText(params.text);
|
||||||
|
|
||||||
if (text) {
|
if (text) {
|
||||||
// Center the text in the viewport
|
// Center the text in the viewport
|
||||||
text.x = penpot.viewport.center.x;
|
text.x = penpot.viewport.center.x;
|
||||||
text.y = penpot.viewport.center.y;
|
text.y = penpot.viewport.center.y;
|
||||||
|
|
||||||
// Select the newly created text
|
// Select the newly created text
|
||||||
penpot.selection = [text];
|
penpot.selection = [text];
|
||||||
|
|
||||||
console.log("Successfully created text:", params.text);
|
console.log("Successfully created text:", params.text);
|
||||||
} else {
|
} else {
|
||||||
console.error("Failed to create text element");
|
console.error("Failed to create text element");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error creating text:", error);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
console.error("Error creating text:", error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the theme in the iframe
|
// Update the theme in the iframe
|
||||||
penpot.on("themechange", (theme) => {
|
penpot.on("themechange", (theme) => {
|
||||||
penpot.ui.sendMessage({
|
penpot.ui.sendMessage({
|
||||||
source: "penpot",
|
source: "penpot",
|
||||||
type: "themechange",
|
type: "themechange",
|
||||||
theme,
|
theme,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
@import "@penpot/plugin-styles/styles.css";
|
@import "@penpot/plugin-styles/styles.css";
|
||||||
|
|
||||||
body {
|
body {
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin-block-end: 0.75rem;
|
margin-block-end: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,27 +1,24 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2020",
|
"target": "ES2020",
|
||||||
"useDefineForClassFields": true,
|
"useDefineForClassFields": true,
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"typeRoots": [
|
"typeRoots": ["./node_modules/@types", "./node_modules/@penpot"],
|
||||||
"./node_modules/@types",
|
"types": ["plugin-types"],
|
||||||
"./node_modules/@penpot"
|
/* Bundler mode */
|
||||||
],
|
"moduleResolution": "bundler",
|
||||||
"types": ["plugin-types"],
|
"allowImportingTsExtensions": true,
|
||||||
/* Bundler mode */
|
"resolveJsonModule": true,
|
||||||
"moduleResolution": "bundler",
|
"isolatedModules": true,
|
||||||
"allowImportingTsExtensions": true,
|
"noEmit": true,
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"noEmit": true,
|
|
||||||
|
|
||||||
/* Linting */
|
/* Linting */
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"noFallthroughCasesInSwitch": true
|
"noFallthroughCasesInSwitch": true
|
||||||
},
|
},
|
||||||
"include": ["src"]
|
"include": ["src"]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,29 +2,29 @@ import { defineConfig } from "vite";
|
|||||||
import livePreview from "vite-live-preview";
|
import livePreview from "vite-live-preview";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
livePreview({
|
livePreview({
|
||||||
reload: true,
|
reload: true,
|
||||||
config: {
|
config: {
|
||||||
build: {
|
build: {
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
build: {
|
||||||
|
rollupOptions: {
|
||||||
|
input: {
|
||||||
|
plugin: "src/plugin.ts",
|
||||||
|
index: "./index.html",
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
entryFileNames: "[name].js",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
build: {
|
|
||||||
rollupOptions: {
|
|
||||||
input: {
|
|
||||||
plugin: "src/plugin.ts",
|
|
||||||
index: "./index.html",
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
entryFileNames: "[name].js",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
preview: {
|
||||||
preview: {
|
port: 4400,
|
||||||
port: 4400,
|
cors: true,
|
||||||
cors: true,
|
},
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user