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