mirror of
https://github.com/penpot/penpot.git
synced 2026-02-18 23:29:12 -05:00
Compare commits
5 Commits
develop
...
alotor-mcp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f634dcf46 | ||
|
|
34840941f3 | ||
|
|
f903194f8f | ||
|
|
bb03d88639 | ||
|
|
8f2eafd060 |
@@ -11,6 +11,7 @@
|
||||
[app.config :as cf]
|
||||
[app.main.data.plugins :as dp]
|
||||
[app.main.repo :as rp]
|
||||
[app.plugins.register :refer [mcp-plugin-id]]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
@@ -20,12 +21,13 @@
|
||||
{:code "plugin.js"
|
||||
:name "Penpot MCP Plugin"
|
||||
:version 2
|
||||
:plugin-id "96dfa740-005d-8020-8007-55ede24a2bae"
|
||||
:plugin-id mcp-plugin-id
|
||||
:description "This plugin enables interaction with the Penpot MCP server"
|
||||
:allow-background true
|
||||
:permissions
|
||||
#{"library:read" "library:write" "comment:read" "content:write" "comment:write"
|
||||
"content:read"}})
|
||||
#{"library:read" "library:write"
|
||||
"comment:read" "comment:write"
|
||||
"content:write" "content:read"}})
|
||||
|
||||
(defn init-mcp!
|
||||
[]
|
||||
|
||||
@@ -261,7 +261,39 @@
|
||||
:else
|
||||
(let [child-id (obj/get child "$id")]
|
||||
(st/emit! (dwt/move-shapes-to-frame #{child-id} id nil nil)
|
||||
(ptk/data-event :layout/update {:ids [id]})))))))
|
||||
(ptk/data-event :layout/update {:ids [id]})))))
|
||||
|
||||
:horizontalSizing
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-item-h-sizing (d/nilv :fix) d/name)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/item-h-sizing-types value))
|
||||
(u/display-not-valid :horizontalPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :horizontalPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-h-sizing value})))))}
|
||||
|
||||
:verticalSizing
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-item-v-sizing (d/nilv :fix) d/name)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/item-v-sizing-types value))
|
||||
(u/display-not-valid :verticalSizing value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :verticalSizing "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-v-sizing value})))))}))
|
||||
|
||||
|
||||
(defn layout-child-proxy? [p]
|
||||
|
||||
@@ -598,3 +598,10 @@
|
||||
(case axis
|
||||
:y "horizontal"
|
||||
:x "vertical"))
|
||||
|
||||
(defn format-geom-rect
|
||||
[{:keys [x y width height]}]
|
||||
#js {:x x
|
||||
:y y
|
||||
:width width
|
||||
:height height})
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
[app.util.object :as obj]
|
||||
[beicon.v2.core :as rx]))
|
||||
|
||||
;; Needs to be here because moving it to `app.main.data.workspace.mcp` will
|
||||
;; cause a circular dependency
|
||||
(def mcp-plugin-id "96dfa740-005d-8020-8007-55ede24a2bae")
|
||||
|
||||
;; Stores the installed plugins information
|
||||
(defonce ^:private registry (atom {}))
|
||||
|
||||
@@ -127,5 +131,6 @@
|
||||
(defn check-permission
|
||||
[plugin-id permission]
|
||||
(or (= plugin-id "00000000-0000-0000-0000-000000000000")
|
||||
(= plugin-id mcp-plugin-id)
|
||||
(let [{:keys [permissions]} (dm/get-in @registry [:data plugin-id])]
|
||||
(contains? permissions permission))))
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.shapes.text :as gst]
|
||||
[app.common.record :as crc]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.types.shape :as cts]
|
||||
@@ -651,4 +652,7 @@
|
||||
(u/display-not-valid :verticalAlign "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:vertical-align value})))))}))
|
||||
(st/emit! (dwt/update-attrs id {:vertical-align value})))))}
|
||||
|
||||
{:name "textBounds"
|
||||
:get #(-> % u/proxy->shape gst/shape->bounds format/format-geom-rect)}))
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import "./style.css";
|
||||
|
||||
const KEEP_ALIVE_TIME = 30000; // 30 seconds
|
||||
|
||||
// get the current theme from the URL
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
document.body.dataset.theme = searchParams.get("theme") ?? "light";
|
||||
@@ -72,8 +74,12 @@ function connectToMcpServer(baseUrl?: string, token?: string): void {
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
console.log("Received from MCP server:", event.data);
|
||||
try {
|
||||
if (event.data === "keep-alive") {
|
||||
// Keep alive response, ignore it
|
||||
return;
|
||||
}
|
||||
console.log("Received from MCP server:", event.data);
|
||||
const request = JSON.parse(event.data);
|
||||
// Forward the task request to the plugin for execution
|
||||
parent.postMessage(request, "*");
|
||||
@@ -82,8 +88,11 @@ function connectToMcpServer(baseUrl?: string, token?: string): void {
|
||||
}
|
||||
};
|
||||
|
||||
const interval = setInterval(() => ws?.send("keep-alive"), KEEP_ALIVE_TIME);
|
||||
|
||||
ws.onclose = (event: CloseEvent) => {
|
||||
console.log("Disconnected from MCP server");
|
||||
clearInterval(interval);
|
||||
const message = event.reason || undefined;
|
||||
updateConnectionStatus("disconnected", "Disconnected", false, message);
|
||||
ws = null;
|
||||
|
||||
@@ -72,6 +72,10 @@ export class PluginBridge {
|
||||
ws.on("message", (data: Buffer) => {
|
||||
this.logger.debug("Received WebSocket message: %s", data.toString());
|
||||
try {
|
||||
if (data.toString() === "keep-alive") {
|
||||
ws.send("keep-alive");
|
||||
return;
|
||||
}
|
||||
const response: PluginTaskResponse<any> = JSON.parse(data.toString());
|
||||
this.handlePluginTaskResponse(response);
|
||||
} catch (error) {
|
||||
|
||||
8
plugins/libs/plugin-types/index.d.ts
vendored
8
plugins/libs/plugin-types/index.d.ts
vendored
@@ -3723,7 +3723,7 @@ export interface ShapeBase extends PluginData {
|
||||
/**
|
||||
* Layout properties for cells in a grid layout.
|
||||
*/
|
||||
readonly layoutCell?: LayoutChildProperties;
|
||||
readonly layoutCell?: LayoutCellProperties;
|
||||
|
||||
/**
|
||||
* Changes the index inside the parent of the current shape.
|
||||
@@ -4127,6 +4127,12 @@ export interface Text extends ShapeBase {
|
||||
*/
|
||||
verticalAlign: 'top' | 'center' | 'bottom' | null;
|
||||
|
||||
/**
|
||||
* Return the bounding box for the text as a (x, y, width, height) rectangle
|
||||
* This is the box that covers the text even if it overflows its selection rectangle.
|
||||
*/
|
||||
readonly textBounds: { x: number; y: number; width: number; height: number };
|
||||
|
||||
/**
|
||||
* Gets a text range within the text shape.
|
||||
* @param start - The start index of the text range.
|
||||
|
||||
Reference in New Issue
Block a user