Compare commits

..

1 Commits

Author SHA1 Message Date
Andrey Antukh
287a8b9ad5 🐛 Fix incorrect plugin icon resolution 2026-02-23 14:42:09 +01:00
32 changed files with 126 additions and 217 deletions

View File

@@ -28,8 +28,8 @@
com.google.guava/guava {:mvn/version "33.4.8-jre"}
funcool/yetti
{:git/tag "v11.9"
:git/sha "5fad7a9"
{:git/tag "v11.8"
:git/sha "1d1b33f"
:git/url "https://github.com/funcool/yetti.git"
:exclusions [org.slf4j/slf4j-api]}

View File

@@ -98,6 +98,7 @@
[:http-server-port {:optional true} ::sm/int]
[:http-server-host {:optional true} :string]
[:http-server-max-body-size {:optional true} ::sm/int]
[:http-server-max-multipart-body-size {:optional true} ::sm/int]
[:http-server-io-threads {:optional true} ::sm/int]
[:http-server-max-worker-threads {:optional true} ::sm/int]

View File

@@ -42,8 +42,8 @@
(def default-params
{::port 6060
::host "0.0.0.0"
::max-body-size 367001600 ; default 350 MiB
})
::max-body-size 31457280 ; default 30 MiB
::max-multipart-body-size 367001600}) ; default 350 MiB
(defmethod ig/expand-key ::server
[k v]
@@ -56,6 +56,7 @@
[::io-threads {:optional true} ::sm/int]
[::max-worker-threads {:optional true} ::sm/int]
[::max-body-size {:optional true} ::sm/int]
[::max-multipart-body-size {:optional true} ::sm/int]
[::router {:optional true} [:fn r/router?]]
[::handler {:optional true} ::sm/fn]])
@@ -78,7 +79,7 @@
{:http/port port
:http/host host
:http/max-body-size (::max-body-size cfg)
:http/max-multipart-body-size (::max-body-size cfg)
:http/max-multipart-body-size (::max-multipart-body-size cfg)
:xnio/direct-buffers false
:xnio/io-threads (::io-threads cfg)
:xnio/max-worker-threads (::max-worker-threads cfg)

View File

@@ -226,10 +226,11 @@
::http/server
{::http/port (cf/get :http-server-port)
::http/host (cf/get :http-server-host)
::http/router (ig/ref ::http/router)
::http/io-threads (cf/get :http-server-io-threads)
::http/max-worker-threads (cf/get :http-server-max-worker-threads)
::http/max-body-size (cf/get :http-server-max-body-size)
::http/router (ig/ref ::http/router)
::http/max-multipart-body-size (cf/get :http-server-max-multipart-body-size)
::mtx/metrics (ig/ref ::mtx/metrics)}
::ldap/provider

View File

@@ -126,12 +126,6 @@ http {
proxy_http_version 1.1;
}
location /plugins {
autoindex on;
alias /home/penpot/penpot/plugins/dist/apps;
proxy_http_version 1.1;
}
location /mcp/ws {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';

View File

@@ -43,12 +43,12 @@
"watch": "exit 0",
"watch:app": "pnpm run clear:shadow-cache && pnpm run build:wasm && concurrently --kill-others-on-fail \"pnpm run watch:app:assets\" \"pnpm run watch:app:main\" \"pnpm run watch:app:libs\"",
"watch:storybook": "pnpm run build:storybook:assets && concurrently --kill-others-on-fail \"storybook dev -p 6006 --no-open\" \"node ./scripts/watch-storybook.js\"",
"postinstall": "(cd ../plugins/libs/plugins-runtime; pnpm run build)"
"postinstall": "(cd ../plugins/libs/plugins-runtime; pnpm install; pnpm run build)"
},
"devDependencies": {
"@penpot/draft-js": "workspace:./packages/draft-js",
"@penpot/mousetrap": "workspace:./packages/mousetrap",
"@penpot/plugins-runtime": "link:../plugins/libs/plugins-runtime",
"@penpot/plugins-runtime": "link:../plugins/dist/plugins-runtime",
"@penpot/svgo": "penpot/svgo#v3.2",
"@penpot/text-editor": "workspace:./text-editor",
"@penpot/tokenscript": "workspace:./packages/tokenscript",

View File

@@ -20,8 +20,8 @@ importers:
specifier: workspace:./packages/mousetrap
version: link:packages/mousetrap
'@penpot/plugins-runtime':
specifier: link:../plugins/libs/plugins-runtime
version: link:../plugins/libs/plugins-runtime
specifier: link:../plugins/dist/plugins-runtime
version: link:../plugins/dist/plugins-runtime
'@penpot/svgo':
specifier: penpot/svgo#v3.2
version: svgo@https://codeload.github.com/penpot/svgo/tar.gz/8c9b0e32e9cb5f106085260bd9375f3c91a5010b

View File

@@ -4,4 +4,9 @@ TARGET=${1:-app};
set -ex
exec pnpm run watch:$TARGET
rm -rf node_modules;
corepack enable;
corepack install;
pnpm install;
pnpm run watch:$TARGET

View File

@@ -66,22 +66,22 @@
(update-in state [:workspace-local :open-plugins] (fnil disj #{}) id))))
(defn- load-plugin!
[{:keys [plugin-id name version description host code icon permissions]}]
[{:keys [plugin-id name description host code icon permissions]}]
(try
(st/emit! (save-current-plugin plugin-id)
(reset-plugin-flags plugin-id))
(.ɵloadPlugin ^js ug/global
#js {:pluginId plugin-id
:name name
:description description
:version version
:host host
:code code
:icon icon
:permissions (apply array permissions)}
(fn []
(st/emit! (remove-current-plugin plugin-id))))
(.ɵloadPlugin
^js ug/global
#js {:pluginId plugin-id
:name name
:description description
:host host
:code code
:icon icon
:permissions (apply array permissions)}
(fn []
(st/emit! (remove-current-plugin plugin-id))))
(catch :default e
(st/emit! (remove-current-plugin plugin-id))

View File

@@ -13,7 +13,7 @@
[rumext.v2 :as mf]))
(mf/defc search-bar*
[{:keys [id class value placeholder icon-id auto-focus on-change on-clear on-submit children]}]
[{:keys [id class value placeholder icon-id auto-focus on-change on-clear children]}]
(let [handle-change
(mf/use-fn
(mf/deps on-change)
@@ -31,19 +31,12 @@
handle-key-down
(mf/use-fn
(mf/deps on-submit)
(fn [event]
(let [enter? (kbd/enter? event)
esc? (kbd/esc? event)
node (dom/get-target event)]
(when ^boolean enter?
(dom/blur! node)
(when (fn? on-submit)
(let [value (dom/get-target-val event)]
(on-submit value event))))
(when ^boolean enter? (dom/blur! node))
(when ^boolean esc? (dom/blur! node)))))]
[:span {:class (stl/css-case :search-box true
:has-children (some? children))}
children

View File

@@ -9,7 +9,6 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.uri :as u]
[app.config :as cfg]
[app.main.data.event :as ev]
[app.main.data.modal :as modal]
@@ -25,7 +24,6 @@
[app.plugins.register :as preg]
[app.util.avatars :as avatars]
[app.util.dom :as dom]
[app.util.globals :as global]
[app.util.i18n :as i18n :refer [tr]]
[beicon.v2.core :as rx]
[cuerdas.core :as str]
@@ -35,19 +33,7 @@
(def ^:private close-icon
(deprecated-icon/icon-xref :close (stl/css :close-icon)))
(defn- normalize-plugin-url
"Automatically appens manifest.json if the plugin-uri comes without it."
[plugin-url]
(if (str/ends-with? plugin-url "manifest.json")
plugin-url
(-> (u/uri plugin-url)
(update :path (fn [path]
(if (str/ends-with? path "/")
(str path "manifest.json")
(str path "/manifest.json"))))
(str))))
(defn- icon-url
(defn icon-url
"Creates an sanitizes de icon URL to display"
[host icon]
(dm/str host
@@ -108,49 +94,48 @@
::mf/register-as :plugin-management}
[]
(let [plugins-state* (mf/use-state #(preg/plugins-list))
plugins-state (deref plugins-state*)
(let [plugins-state* (mf/use-state #(preg/plugins-list))
plugins-state (deref plugins-state*)
plugin-url* (mf/use-state "")
plugin-url (deref plugin-url*)
plugin-url* (mf/use-state "")
plugin-url (deref plugin-url*)
input-status* (mf/use-state nil) ;; :error-url :error-manifest :success
input-status (deref input-status*)
fetching-manifest? (mf/use-state false)
error-url? (= :error-url input-status)
input-status* (mf/use-state nil) ;; :error-url :error-manifest :success
input-status @input-status*
error-url? (= :error-url input-status)
error-manifest? (= :error-manifest input-status)
error? (or error-url? error-manifest?)
error? (or error-url? error-manifest?)
permissions (mf/deref refs/permissions)
user-can-edit? (get permissions :can-edit)
user-can-edit? (:can-edit (deref refs/permissions))
fetching-manifest?
(mf/use-state false)
on-url-change
handle-url-input
(mf/use-fn
(fn [value]
(reset! input-status* nil)
(reset! plugin-url* value)))
on-install
handle-install-click
(mf/use-fn
(mf/deps plugins-state plugin-url)
(fn []
(reset! fetching-manifest? true)
(->> (dp/fetch-manifest (normalize-plugin-url plugin-url))
(->> (dp/fetch-manifest plugin-url)
(rx/subs!
(fn [plugin]
(reset! fetching-manifest? false)
(if plugin
(do
(st/emit! (ptk/event ::ev/event {::ev/name "install-plugin" :name (:name plugin) :url plugin-url}))
(modal/show! :plugin-permissions
{:plugin plugin
:on-accept
#(do
(preg/install-plugin! plugin)
(modal/show! :plugin-management {}))})
(modal/show!
:plugin-permissions
{:plugin plugin
:on-accept
#(do
(preg/install-plugin! plugin)
(modal/show! :plugin-management {}))})
(reset! input-status* :success)
(reset! plugin-url* ""))
;; Cannot get the manifest
@@ -160,7 +145,7 @@
(reset! fetching-manifest? false)
(reset! input-status* :error-url))))))
on-open-plugin
handle-open-plugin
(mf/use-fn
(fn [manifest]
(st/emit! (ptk/event ::ev/event {::ev/name "start-plugin"
@@ -170,7 +155,7 @@
(dp/open-plugin! manifest user-can-edit?)
(modal/hide!)))
on-remove-plugin
handle-remove-plugin
(mf/use-fn
(mf/deps plugins-state)
(fn [plugin-index]
@@ -190,16 +175,14 @@
[:div {:class (stl/css :modal-content)}
[:div {:class (stl/css :top-bar)}
[:> search-bar* {:on-change on-url-change
:on-submit on-install
[:> search-bar* {:on-change handle-url-input
:value plugin-url
:placeholder (tr "workspace.plugins.search-placeholder")
:class (stl/css-case :input-error error?)}]
[:button {:class (stl/css :primary-button)
:disabled @fetching-manifest?
:on-click on-install}
(tr "workspace.plugins.install")]]
:on-click handle-install-click} (tr "workspace.plugins.install")]]
(when error-url?
[:div {:class (stl/css-case :info true :error error?)}
@@ -237,11 +220,10 @@
:index idx
:manifest manifest
:user-can-edit user-can-edit?
:on-open-plugin on-open-plugin
:on-remove-plugin on-remove-plugin}])]])]]]))
:on-open-plugin handle-open-plugin
:on-remove-plugin handle-remove-plugin}])]])]]]))
(mf/defc plugins-permission-list*
{::mf/private true}
(mf/defc plugins-permission-list
[{:keys [permissions]}]
[:div {:class (stl/css :permissions-list)}
(cond
@@ -309,45 +291,36 @@
::mf/register-as :plugin-permissions}
[{:keys [plugin on-accept on-close]}]
(let [host
(:host plugin)
(let [{:keys [host permissions]} plugin
permissions (set permissions)
permissions
(-> plugin :permissions set)
on-accept-dialog
handle-accept-dialog
(mf/use-fn
(mf/deps on-accept)
(fn [event]
(dom/prevent-default event)
(st/emit! (ev/event {::ev/name "allow-plugin-permissions"
:host host
:permissions (str/join ", " permissions)})
(st/emit! (ptk/event ::ev/event {::ev/name "allow-plugin-permissions"
:host host
:permissions (->> permissions (str/join ", "))})
(modal/hide))
(when on-accept (on-accept))))
on-close-dialog
handle-close-dialog
(mf/use-fn
(mf/deps on-close)
(fn [event]
(dom/prevent-default event)
(st/emit! (ev/event {::ev/name "reject-plugin-permissions"
:host host
:permissions (str/join ", " permissions)})
(st/emit! (ptk/event ::ev/event {::ev/name "reject-plugin-permissions"
:host host
:permissions (->> permissions (str/join ", "))})
(modal/hide))
(when on-close (on-close))))]
(mf/with-effect [on-accept-dialog]
(.addEventListener ^js global/document "keydown" on-accept-dialog)
#(.removeEventListener ^js global/document "keydown" on-accept-dialog))
[:div {:class (stl/css :modal-overlay)}
[:div {:class (stl/css :modal-dialog :plugin-permissions)}
[:button {:class (stl/css :close-btn) :on-click on-close-dialog} close-icon]
[:button {:class (stl/css :close-btn) :on-click handle-close-dialog} close-icon]
[:div {:class (stl/css :modal-title)} (tr "workspace.plugins.permissions.title" (str/upper (:name plugin)))]
[:div {:class (stl/css :modal-content)}
[:> plugins-permission-list* {:permissions permissions}]
[:& plugins-permission-list {:permissions permissions}]
(when-not (contains? cfg/plugins-whitelist host)
[:div {:class (stl/css :permissions-disclaimer)}
@@ -359,13 +332,13 @@
{:class (stl/css :cancel-button :button-expand)
:type "button"
:value (tr "ds.confirm-cancel")
:on-click on-close-dialog}]
:on-click handle-close-dialog}]
[:input
{:class (stl/css :primary-button :button-expand)
:type "button"
:value (tr "ds.confirm-allow")
:on-click on-accept-dialog}]]]]]))
:on-click handle-accept-dialog}]]]]]))
(mf/defc plugins-permissions-updated-dialog
@@ -373,15 +346,11 @@
::mf/register-as :plugin-permissions-update}
[{:keys [plugin on-accept on-close]}]
(let [host
(:host plugin)
(let [{:keys [host permissions]} plugin
permissions (set permissions)
permissions
(-> plugin :permissions set)
on-accept-dialog
handle-accept-dialog
(mf/use-fn
(mf/deps on-accept)
(fn [event]
(dom/prevent-default event)
(st/emit! (ptk/event ::ev/event {::ev/name "allow-plugin-permissions"
@@ -390,9 +359,8 @@
(modal/hide))
(when on-accept (on-accept))))
on-close-dialog
handle-close-dialog
(mf/use-fn
(mf/deps on-close)
(fn [event]
(dom/prevent-default event)
(st/emit! (ptk/event ::ev/event {::ev/name "reject-plugin-permissions"
@@ -401,20 +369,16 @@
(modal/hide))
(when on-close (on-close))))]
(mf/with-effect [on-accept-dialog]
(.addEventListener ^js global/document "keydown" on-accept-dialog)
#(.removeEventListener ^js global/document "keydown" on-accept-dialog))
[:div {:class (stl/css :modal-overlay)}
[:div {:class (stl/css :modal-dialog :plugin-permissions)}
[:button {:class (stl/css :close-btn) :on-click on-close-dialog} close-icon]
[:button {:class (stl/css :close-btn) :on-click handle-close-dialog} close-icon]
[:div {:class (stl/css :modal-title)}
(tr "workspace.plugins.permissions-update.title" (str/upper (:name plugin)))]
[:div {:class (stl/css :modal-content)}
[:div {:class (stl/css :modal-paragraph)}
(tr "workspace.plugins.permissions-update.warning")]
[:> plugins-permission-list* {:permissions permissions}]]
[:& plugins-permission-list {:permissions permissions}]]
[:div {:class (stl/css :modal-footer)}
[:div {:class (stl/css :action-buttons)}
@@ -422,13 +386,13 @@
{:class (stl/css :cancel-button :button-expand)
:type "button"
:value (tr "ds.confirm-cancel")
:on-click on-close-dialog}]
:on-click handle-close-dialog}]
[:input
{:class (stl/css :primary-button :button-expand)
:type "button"
:value (tr "ds.confirm-allow")
:on-click on-accept-dialog}]]]]]))
:on-click handle-accept-dialog}]]]]]))
(mf/defc plugins-try-out-dialog
@@ -438,31 +402,25 @@
(let [{:keys [icon host name]} plugin
on-accept-dialog
handle-accept-dialog
(mf/use-fn
(mf/deps on-accept)
(fn [event]
(dom/prevent-default event)
(st/emit! (ptk/event ::ev/event {::ev/name "try-out-accept"})
(modal/hide))
(when on-accept (on-accept))))
on-close-dialog
handle-close-dialog
(mf/use-fn
(mf/deps on-close)
(fn [event]
(dom/prevent-default event)
(st/emit! (ptk/event ::ev/event {::ev/name "try-out-cancel"})
(modal/hide))
(when on-close (on-close))))]
(mf/with-effect [on-accept-dialog]
(.addEventListener ^js global/document "keydown" on-accept-dialog)
#(.removeEventListener ^js global/document "keydown" on-accept-dialog))
[:div {:class (stl/css :modal-overlay)}
[:div {:class (stl/css :modal-dialog :plugin-try-out)}
[:button {:class (stl/css :close-btn) :on-click on-close-dialog} close-icon]
[:button {:class (stl/css :close-btn) :on-click handle-close-dialog} close-icon]
[:div {:class (stl/css :modal-title)}
[:div {:class (stl/css :plugin-icon)}
[:img {:src (if (some? icon)
@@ -480,10 +438,10 @@
{:class (stl/css :cancel-button :button-expand)
:type "button"
:value (tr "workspace.plugins.try-out.cancel")
:on-click on-close-dialog}]
:on-click handle-close-dialog}]
[:input
{:class (stl/css :primary-button :button-expand)
:type "button"
:value (tr "workspace.plugins.try-out.try")
:on-click on-accept-dialog}]]]]]))
:on-click handle-accept-dialog}]]]]]))

View File

@@ -58,7 +58,7 @@
origin
(if (= vers 1)
(-> plugin-url
(assoc :path "/")
(assoc :path "")
(str))
(-> plugin-url
(u/join ".")
@@ -78,7 +78,6 @@
(d/without-nils
{:plugin-id plugin-id
:url (str plugin-url)
:version vers
:name name
:description desc
:host origin

View File

@@ -12,10 +12,7 @@
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": {
"base": "dist/apps/contrast-plugin",
"browser": "",
},
"outputPath": "dist/apps/contrast-plugin",
"index": "apps/contrast-plugin/src/index.html",
"browser": "apps/contrast-plugin/src/main.ts",
"polyfills": ["zone.js"],
@@ -23,7 +20,6 @@
"assets": [
"apps/contrast-plugin/src/_headers",
"apps/contrast-plugin/src/favicon.ico",
"apps/contrast-plugin/src/manifest.json",
"apps/contrast-plugin/src/assets"
],
"styles": [
@@ -222,10 +218,7 @@
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": {
"base": "dist/apps/table-plugin",
"browser": ""
},
"outputPath": "dist/apps/table-plugin",
"index": "apps/table-plugin/src/index.html",
"browser": "apps/table-plugin/src/main.ts",
"polyfills": ["zone.js"],
@@ -233,7 +226,6 @@
"assets": [
"apps/table-plugin/src/_headers",
"apps/table-plugin/src/favicon.ico",
"apps/table-plugin/src/manifest.json",
"apps/table-plugin/src/assets"
],
"styles": [
@@ -364,10 +356,7 @@
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": {
"base": "dist/apps/colors-to-tokens-plugin",
"browser": ""
},
"outputPath": "dist/apps/colors-to-tokens-plugin",
"index": "apps/colors-to-tokens-plugin/src/index.html",
"browser": "apps/colors-to-tokens-plugin/src/main.ts",
"polyfills": ["zone.js"],
@@ -375,7 +364,6 @@
"assets": [
"apps/colors-to-tokens-plugin/src/_headers",
"apps/colors-to-tokens-plugin/src/favicon.ico",
"apps/colors-to-tokens-plugin/src/manifest.json",
"apps/colors-to-tokens-plugin/src/assets"
],
"styles": [

View File

@@ -7,9 +7,9 @@
"build": "ng build colors-to-tokens-plugin && pnpm run build:plugin",
"build:dev": "ng build colors-to-tokens-plugin --configuration development",
"build:plugin": "node ../../tools/scripts/build-plugin.mjs --plugin=colors-to-tokens-plugin",
"watch": "node ../../tools/scripts/build-plugin.mjs --plugin=colors-to-tokens-plugin --watch",
"build:plugin:watch": "node ../../tools/scripts/build-plugin.mjs --plugin=colors-to-tokens-plugin --watch",
"serve": "ng serve colors-to-tokens-plugin",
"init": "concurrently --kill-others --names plugin,serve \"pnpm run watch\" \"pnpm run serve\"",
"init": "concurrently --kill-others --names plugin,serve \"pnpm run build:plugin:watch\" \"pnpm run serve\"",
"lint": "eslint .",
"test": "vitest"
}

View File

@@ -1,8 +1,6 @@
import { ApplicationConfig } from '@angular/core';
import { provideRouter, withHashLocation } from '@angular/router';
import { provideRouter } from '@angular/router';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter([], withHashLocation())
],
};
providers: [provideRouter([])],
};

View File

@@ -1,8 +1,7 @@
{
"name": "Colors to Tokens",
"description": "Generate a design tokens file from a list of colors",
"version": 2,
"code": "assets/plugin.js",
"icon": "assets/icon.png",
"code": "/assets/plugin.js",
"icon": "/assets/icon.png",
"permissions": ["content:read", "library:read", "allow:downloads"]
}

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="utf-8" />
<title>colors-to-tokens-plugin</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>

View File

@@ -7,9 +7,9 @@
"build": "ng build contrast-plugin && pnpm run build:plugin",
"build:dev": "ng build contrast-plugin --configuration development",
"build:plugin": "node ../../tools/scripts/build-plugin.mjs --plugin=contrast-plugin",
"watch": "node ../../tools/scripts/build-plugin.mjs --plugin=contrast-plugin --watch",
"build:plugin:watch": "node ../../tools/scripts/build-plugin.mjs --plugin=contrast-plugin --watch",
"serve": "ng serve contrast-plugin",
"init": "concurrently --kill-others --names plugin,serve \"pnpm run watch\" \"pnpm run serve\"",
"init": "concurrently --kill-others --names plugin,serve \"pnpm run build:plugin:watch\" \"pnpm run serve\"",
"lint": "eslint .",
"test": "vitest"
}

View File

@@ -1,6 +1,6 @@
import { ApplicationConfig } from '@angular/core';
import { provideRouter, withHashLocation } from '@angular/router';
import { provideRouter } from '@angular/router';
export const appConfig: ApplicationConfig = {
providers: [provideRouter([], withHashLocation())],
providers: [provideRouter([])],
};

View File

@@ -1,8 +1,7 @@
{
"name": "Contrast",
"description": "Measure contrast plugin",
"version": 2,
"code": "assets/plugin.js",
"icon": "assets/icon.png",
"code": "/assets/plugin.js",
"icon": "/assets/icon.png",
"permissions": ["content:read"]
}

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="utf-8" />
<title>contrast-plugin</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>

View File

@@ -7,9 +7,9 @@
"build": "ng build table-plugin && pnpm run build:plugin",
"build:dev": "ng build table-plugin --configuration development",
"build:plugin": "node ../../tools/scripts/build-plugin.mjs --plugin=table-plugin",
"watch": "node ../../tools/scripts/build-plugin.mjs --plugin=table-plugin --watch",
"build:plugin:watch": "node ../../tools/scripts/build-plugin.mjs --plugin=table-plugin --watch",
"serve": "ng serve table-plugin",
"init": "concurrently --kill-others --names plugin,serve \"pnpm run watch\" \"pnpm run serve\"",
"init": "concurrently --kill-others --names plugin,serve \"pnpm run build:plugin:watch\" \"pnpm run serve\"",
"lint": "eslint .",
"test": "vitest"
}

View File

@@ -1,7 +1,7 @@
import { ApplicationConfig } from '@angular/core';
import { provideRouter, withHashLocation } from '@angular/router';
import { provideRouter } from '@angular/router';
import { appRoutes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [provideRouter(appRoutes, withHashLocation())],
providers: [provideRouter(appRoutes)],
};

View File

@@ -1,8 +1,7 @@
{
"name": "Table plugin",
"description": "Table plugin to import or create tables",
"version": 2,
"code": "assets/plugin.js",
"icon": "assets/icon.png",
"code": "/assets/plugin.js",
"icon": "/assets/icon.png",
"permissions": ["content:read", "content:write"]
}

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="utf-8" />
<title>table-plugin</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>

View File

@@ -1,6 +0,0 @@
/// <reference types="vitest/config" />
import { defineConfig } from 'vite';
export default defineConfig({
root: "./"
});

View File

@@ -6,7 +6,6 @@ export const manifestSchema = z.object({
host: z.string().url(),
code: z.string(),
icon: z.string().optional(),
version: z.number().optional(),
description: z.string().max(200).optional(),
permissions: z.array(
z.enum([

View File

@@ -2,5 +2,5 @@ import { z } from 'zod';
export const openUISchema = z.object({
width: z.number().positive(),
height: z.number().positive()
height: z.number().positive(),
});

View File

@@ -1,28 +1,8 @@
import { Manifest } from './models/manifest.model.js';
import { manifestSchema } from './models/manifest.schema.js';
export function getValidUrl(host: string, path: string): URL {
return new URL(path, host);
}
export function prepareUrl(manifest: Manifest, url: string, params: Object): string {
const result = getValidUrl(manifest.host, url);
for (let [k, v] of Object.entries(params)) {
if (!result.searchParams.has(k)) {
result.searchParams.set(k, v);
}
}
if (manifest.version === undefined || manifest.version === 1) {
return result.toString();
} else if (manifest.version === 2) {
const queryString = result.searchParams.toString();
result.search = "";
result.hash = `/?${queryString}`;
return result.toString();
} else {
throw new Error("invalid manifest version");
}
export function getValidUrl(host: string, path: string): string {
return new URL(path, host).toString();
}
export function loadManifest(url: string): Promise<Manifest> {

View File

@@ -1,6 +1,6 @@
import { describe, it, vi, expect, beforeEach, afterEach } from 'vitest';
import { createPluginManager } from './plugin-manager';
import { loadManifestCode, getValidUrl, prepareUrl } from './parse-manifest.js';
import { loadManifestCode, getValidUrl } from './parse-manifest.js';
import { PluginModalElement } from './modal/plugin-modal.js';
import { openUIApi } from './api/openUI.api.js';
import type { Context, Theme } from '@penpot/plugin-types';
@@ -9,7 +9,6 @@ import type { Manifest } from './models/manifest.model.js';
vi.mock('./parse-manifest.js', () => ({
loadManifestCode: vi.fn(),
getValidUrl: vi.fn(),
prepareUrl: vi.fn(),
}));
vi.mock('./api/openUI.api.js', () => ({
@@ -72,8 +71,7 @@ describe('createPluginManager', () => {
vi.mocked(loadManifestCode).mockResolvedValue(
'console.log("Plugin loaded");',
);
vi.mocked(getValidUrl).mockReturnValue(new URL('https://example.com/plugin'));
vi.mocked(prepareUrl).mockReturnValue('https://example.com/plugin');
vi.mocked(getValidUrl).mockReturnValue('https://example.com/plugin');
});
afterEach(() => {
@@ -112,7 +110,7 @@ describe('createPluginManager', () => {
height: 300,
});
expect(prepareUrl).toHaveBeenCalledWith(manifest, '/test-url', {theme: "light"});
expect(getValidUrl).toHaveBeenCalledWith(manifest.host, '/test-url');
expect(openUIApi).toHaveBeenCalledWith(
'Test Modal',
'https://example.com/plugin',

View File

@@ -1,6 +1,6 @@
import type { Context, Theme } from '@penpot/plugin-types';
import { prepareUrl, loadManifestCode } from './parse-manifest.js';
import { getValidUrl, loadManifestCode } from './parse-manifest.js';
import { Manifest } from './models/manifest.model.js';
import { PluginModalElement } from './modal/plugin-modal.js';
import { openUIApi } from './api/openUI.api.js';
@@ -8,7 +8,6 @@ import { OpenUIOptions } from './models/open-ui-options.model.js';
import { RegisterListener } from './models/plugin.model.js';
import { openUISchema } from './models/open-ui-options.schema.js';
export async function createPluginManager(
context: Context,
manifest: Manifest,
@@ -81,8 +80,9 @@ export async function createPluginManager(
};
const openModal = (name: string, url: string, options?: OpenUIOptions) => {
const theme = context.theme as Theme;
const modalUrl = prepareUrl(manifest, url, {theme});
const theme = context.theme as 'light' | 'dark';
const modalUrl = getValidUrl(manifest.host, url);
if (modal?.getAttribute('iframe-src') === modalUrl) {
return;

View File

@@ -35,7 +35,7 @@ export default defineConfig({
// Configuration for building your library.
// See: https://vitejs.dev/guide/build.html#library-mode
build: {
outDir: './dist/',
outDir: '../../dist/plugins-runtime',
reportCompressedSize: true,
commonjsOptions: {
transformMixedEsModules: true,