mirror of
https://github.com/penpot/penpot.git
synced 2026-02-23 10:17:35 -05:00
Compare commits
17 Commits
eva-refact
...
niwinz-plu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6284ed9e3f | ||
|
|
2a323ffaca | ||
|
|
84281d1686 | ||
|
|
11e233c0c5 | ||
|
|
51458d5afe | ||
|
|
a0fc224533 | ||
|
|
a0f6bee78c | ||
|
|
1544b8082a | ||
|
|
8045b00be5 | ||
|
|
edc9a61933 | ||
|
|
1b8afccba2 | ||
|
|
dd856ecf50 | ||
|
|
145198c148 | ||
|
|
eddfc4c4b2 | ||
|
|
e6e34af391 | ||
|
|
3d41dc276e | ||
|
|
cee974a906 |
@@ -2,6 +2,9 @@
|
||||
|
||||
## 2.14.0 (Unreleased)
|
||||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
- Deprecate `PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE` in favour of `PENPOT_HTTP_SERVER_MAX_BODY_SIZE`.
|
||||
|
||||
### :sparkles: New features & Enhancements
|
||||
|
||||
- Access to design tokens in Penpot Plugins [Taiga #8990](https://tree.taiga.io/project/penpot/us/8990)
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
com.google.guava/guava {:mvn/version "33.4.8-jre"}
|
||||
|
||||
funcool/yetti
|
||||
{:git/tag "v11.8"
|
||||
:git/sha "1d1b33f"
|
||||
{:git/tag "v11.9"
|
||||
:git/sha "5fad7a9"
|
||||
:git/url "https://github.com/funcool/yetti.git"
|
||||
:exclusions [org.slf4j/slf4j-api]}
|
||||
|
||||
|
||||
@@ -98,7 +98,6 @@
|
||||
[: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]
|
||||
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
(def default-params
|
||||
{::port 6060
|
||||
::host "0.0.0.0"
|
||||
::max-body-size 31457280 ; default 30 MiB
|
||||
::max-multipart-body-size 367001600}) ; default 350 MiB
|
||||
::max-body-size 367001600 ; default 350 MiB
|
||||
})
|
||||
|
||||
(defmethod ig/expand-key ::server
|
||||
[k v]
|
||||
@@ -56,7 +56,6 @@
|
||||
[::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]])
|
||||
|
||||
@@ -79,7 +78,7 @@
|
||||
{:http/port port
|
||||
:http/host host
|
||||
:http/max-body-size (::max-body-size cfg)
|
||||
:http/max-multipart-body-size (::max-multipart-body-size cfg)
|
||||
:http/max-multipart-body-size (::max-body-size cfg)
|
||||
:xnio/direct-buffers false
|
||||
:xnio/io-threads (::io-threads cfg)
|
||||
:xnio/max-worker-threads (::max-worker-threads cfg)
|
||||
|
||||
@@ -226,11 +226,10 @@
|
||||
::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/max-multipart-body-size (cf/get :http-server-max-multipart-body-size)
|
||||
::http/router (ig/ref ::http/router)
|
||||
::mtx/metrics (ig/ref ::mtx/metrics)}
|
||||
|
||||
::ldap/provider
|
||||
|
||||
@@ -50,6 +50,7 @@ services:
|
||||
- 4400:4400
|
||||
- 4401:4401
|
||||
- 4402:4402
|
||||
- 4403:4403
|
||||
|
||||
# Plugins
|
||||
- 4200:4200
|
||||
|
||||
@@ -126,6 +126,12 @@ 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';
|
||||
|
||||
@@ -30,11 +30,9 @@ x-uri: &penpot-public-uri
|
||||
PENPOT_PUBLIC_URI: http://localhost:9001
|
||||
|
||||
x-body-size: &penpot-http-body-size
|
||||
# Max body size (30MiB); Used for plain requests, should never be
|
||||
# greater than multi-part size
|
||||
PENPOT_HTTP_SERVER_MAX_BODY_SIZE: 31457280
|
||||
|
||||
# Max multipart body size (350MiB)
|
||||
# Max body size
|
||||
PENPOT_HTTP_SERVER_MAX_BODY_SIZE: 367001600
|
||||
# Deprecation warning: this variable is deprecated. Use PENPOT_HTTP_SERVER_MAX_BODY (defaults to 367001600)
|
||||
PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE: 367001600
|
||||
|
||||
## Penpot SECRET KEY. It serves as a master key from which other keys for subsystems
|
||||
|
||||
@@ -30,8 +30,8 @@ update_flags /var/www/app/js/config.js
|
||||
export PENPOT_BACKEND_URI=${PENPOT_BACKEND_URI:-http://penpot-backend:6060}
|
||||
export PENPOT_EXPORTER_URI=${PENPOT_EXPORTER_URI:-http://penpot-exporter:6061}
|
||||
export PENPOT_NITRATE_URI=${PENPOT_NITRATE_URI:-http://penpot-nitrate:3000}
|
||||
export PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE=${PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE:-367001600} # Default to 350MiB
|
||||
envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_NITRATE_URI,\$PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE" \
|
||||
export PENPOT_HTTP_SERVER_MAX_BODY_SIZE=${PENPOT_HTTP_SERVER_MAX_BODY_SIZE:-367001600} # Default to 350MiB
|
||||
envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_NITRATE_URI,\$PENPOT_HTTP_SERVER_MAX_BODY_SIZE" \
|
||||
< /tmp/nginx.conf.template > /etc/nginx/nginx.conf
|
||||
|
||||
PENPOT_DEFAULT_INTERNAL_RESOLVER="$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf)"
|
||||
|
||||
@@ -76,7 +76,7 @@ http {
|
||||
listen [::]:8080 default_server;
|
||||
server_name _;
|
||||
|
||||
client_max_body_size $PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE;
|
||||
client_max_body_size $PENPOT_HTTP_SERVER_MAX_BODY_SIZE;
|
||||
charset utf-8;
|
||||
|
||||
etag off;
|
||||
|
||||
@@ -188,8 +188,8 @@ server {
|
||||
server_name penpot.mycompany.com;
|
||||
|
||||
# This value should be in sync with the corresponding in the docker-compose.yml
|
||||
# PENPOT_HTTP_SERVER_MAX_BODY_SIZE: 31457280
|
||||
client_max_body_size 31457280;
|
||||
# PENPOT_HTTP_SERVER_MAX_BODY_SIZE: 367001600
|
||||
client_max_body_size 367001600;
|
||||
|
||||
# Logs: Configure your logs following the best practices inside your company
|
||||
access_log /path/to/penpot.access.log;
|
||||
|
||||
@@ -42,12 +42,13 @@
|
||||
"clear:shadow-cache": "rm -rf .shadow-cljs",
|
||||
"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\""
|
||||
"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)"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@penpot/draft-js": "workspace:./packages/draft-js",
|
||||
"@penpot/mousetrap": "workspace:./packages/mousetrap",
|
||||
"@penpot/plugins-runtime": "1.4.2",
|
||||
"@penpot/plugins-runtime": "link:../plugins/libs/plugins-runtime",
|
||||
"@penpot/svgo": "penpot/svgo#v3.2",
|
||||
"@penpot/text-editor": "workspace:./text-editor",
|
||||
"@penpot/tokenscript": "workspace:./packages/tokenscript",
|
||||
|
||||
47
frontend/pnpm-lock.yaml
generated
47
frontend/pnpm-lock.yaml
generated
@@ -20,8 +20,8 @@ importers:
|
||||
specifier: workspace:./packages/mousetrap
|
||||
version: link:packages/mousetrap
|
||||
'@penpot/plugins-runtime':
|
||||
specifier: 1.4.2
|
||||
version: 1.4.2
|
||||
specifier: link:../plugins/libs/plugins-runtime
|
||||
version: link:../plugins/libs/plugins-runtime
|
||||
'@penpot/svgo':
|
||||
specifier: penpot/svgo#v3.2
|
||||
version: svgo@https://codeload.github.com/penpot/svgo/tar.gz/8c9b0e32e9cb5f106085260bd9375f3c91a5010b
|
||||
@@ -581,15 +581,6 @@ packages:
|
||||
'@dabh/diagnostics@2.0.8':
|
||||
resolution: {integrity: sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==}
|
||||
|
||||
'@endo/cache-map@1.1.0':
|
||||
resolution: {integrity: sha512-owFGshs/97PDw9oguZqU/px8Lv1d0KjAUtDUiPwKHNXRVUE/jyettEbRoTbNJR1OaI8biMn6bHr9kVJsOh6dXw==}
|
||||
|
||||
'@endo/env-options@1.1.11':
|
||||
resolution: {integrity: sha512-p9OnAPsdqoX4YJsE98e3NBVhIr2iW9gNZxHhAI2/Ul5TdRfoOViItzHzTqrgUVopw6XxA1u1uS6CykLMDUxarA==}
|
||||
|
||||
'@endo/immutable-arraybuffer@1.1.2':
|
||||
resolution: {integrity: sha512-u+NaYB2aqEugQ3u7w3c5QNkPogf8q/xGgsPaqdY6pUiGWtYiTiFspKFcha6+oeZhWXWQ23rf0KrUq0kfuzqYyQ==}
|
||||
|
||||
'@esbuild/aix-ppc64@0.21.5':
|
||||
resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -1258,12 +1249,6 @@ packages:
|
||||
resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
|
||||
'@penpot/plugin-types@1.4.2':
|
||||
resolution: {integrity: sha512-O8wU6RSYE8bIVU7g8cSTYi32ppxs3R13dq7X3Nn9tmDaJjBOKOBpVLuoRPIp3fJC65fv8/7om0sdrtFoL5v19g==}
|
||||
|
||||
'@penpot/plugins-runtime@1.4.2':
|
||||
resolution: {integrity: sha512-y9TDZOnb96JBW9E33dHKpmTMeAPXLtHDIZruUVjtM8hBJWZK7RCv+vAGDGxeoZJC/OB2YAHrCZG+mukePBzcuQ==}
|
||||
|
||||
'@pkgjs/parseargs@0.11.0':
|
||||
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
|
||||
engines: {node: '>=14'}
|
||||
@@ -4636,9 +4621,6 @@ packages:
|
||||
resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
ses@1.14.0:
|
||||
resolution: {integrity: sha512-T07hNgOfVRTLZGwSS50RnhqrG3foWP+rM+Q5Du4KUQyMLFI3A8YA4RKl0jjZzhihC1ZvDGrWi/JMn4vqbgr/Jg==}
|
||||
|
||||
set-function-length@1.2.2:
|
||||
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -5499,9 +5481,6 @@ packages:
|
||||
peerDependencies:
|
||||
zod: ^3.25.0 || ^4.0.0
|
||||
|
||||
zod@3.25.76:
|
||||
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
|
||||
|
||||
zod@4.3.6:
|
||||
resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==}
|
||||
|
||||
@@ -5775,12 +5754,6 @@ snapshots:
|
||||
enabled: 2.0.0
|
||||
kuler: 2.0.0
|
||||
|
||||
'@endo/cache-map@1.1.0': {}
|
||||
|
||||
'@endo/env-options@1.1.11': {}
|
||||
|
||||
'@endo/immutable-arraybuffer@1.1.2': {}
|
||||
|
||||
'@esbuild/aix-ppc64@0.21.5':
|
||||
optional: true
|
||||
|
||||
@@ -6297,14 +6270,6 @@ snapshots:
|
||||
'@parcel/watcher-win32-x64': 2.5.6
|
||||
optional: true
|
||||
|
||||
'@penpot/plugin-types@1.4.2': {}
|
||||
|
||||
'@penpot/plugins-runtime@1.4.2':
|
||||
dependencies:
|
||||
'@penpot/plugin-types': 1.4.2
|
||||
ses: 1.14.0
|
||||
zod: 3.25.76
|
||||
|
||||
'@pkgjs/parseargs@0.11.0':
|
||||
optional: true
|
||||
|
||||
@@ -10000,12 +9965,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
ses@1.14.0:
|
||||
dependencies:
|
||||
'@endo/cache-map': 1.1.0
|
||||
'@endo/env-options': 1.1.11
|
||||
'@endo/immutable-arraybuffer': 1.1.2
|
||||
|
||||
set-function-length@1.2.2:
|
||||
dependencies:
|
||||
define-data-property: 1.1.4
|
||||
@@ -10974,6 +10933,4 @@ snapshots:
|
||||
dependencies:
|
||||
zod: 4.3.6
|
||||
|
||||
zod@3.25.76: {}
|
||||
|
||||
zod@4.3.6: {}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<meta name="twitter:creator" content="@penpotapp">
|
||||
<meta name="theme-color" content="#FFFFFF" media="(prefers-color-scheme: light)">
|
||||
<link id="theme" href="css/main.css?version={{& version_tag}}" rel="stylesheet" type="text/css" />
|
||||
<link href="css/ui.css?ts={{& ts}}" rel="stylesheet" type="text/css" />
|
||||
<link href="css/ui.css?ts={{& version_tag}}" rel="stylesheet" type="text/css" />
|
||||
{{#isDebug}}
|
||||
<link href="css/debug.css?version={{& version_tag}}" rel="stylesheet" type="text/css" />
|
||||
{{/isDebug}}
|
||||
|
||||
@@ -66,22 +66,22 @@
|
||||
(update-in state [:workspace-local :open-plugins] (fnil disj #{}) id))))
|
||||
|
||||
(defn- load-plugin!
|
||||
[{:keys [plugin-id name description host code icon permissions]}]
|
||||
[{:keys [plugin-id name version 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
|
||||
: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
|
||||
:version version
|
||||
: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))
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
Structured tokens are non-primitive token types like `typography` or `box-shadow`."
|
||||
[^js token-symbol]
|
||||
(if (instance? js/Array (.-value token-symbol))
|
||||
(mapv structured-token->penpot-map (.-value token-symbol))
|
||||
(mapv tokenscript-symbols->penpot-unit (.-value token-symbol))
|
||||
(let [entries (es6-iterator-seq (.entries (.-value token-symbol)))]
|
||||
(into {} (map (fn [[k v :as V]]
|
||||
[(keyword k) (tokenscript-symbols->penpot-unit v)])
|
||||
@@ -88,7 +88,7 @@
|
||||
(defn tokenscript-symbols->penpot-unit [^js v]
|
||||
(cond
|
||||
(structured-token? v) (structured-token->penpot-map v)
|
||||
(list-symbol? v) (tokenscript-symbols->penpot-unit (.nth 1 v))
|
||||
(list-symbol? v) (structured-token->penpot-map v)
|
||||
(color-symbol? v) (.-value (.to v "hex"))
|
||||
(rem-number-with-unit? v) (rem->px v)
|
||||
:else (.-value v)))
|
||||
|
||||
@@ -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 children]}]
|
||||
[{:keys [id class value placeholder icon-id auto-focus on-change on-clear on-submit children]}]
|
||||
(let [handle-change
|
||||
(mf/use-fn
|
||||
(mf/deps on-change)
|
||||
@@ -31,12 +31,19 @@
|
||||
|
||||
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 ^boolean enter?
|
||||
(dom/blur! node)
|
||||
(when (fn? on-submit)
|
||||
(let [value (dom/get-target-val event)]
|
||||
(on-submit value event))))
|
||||
|
||||
(when ^boolean esc? (dom/blur! node)))))]
|
||||
|
||||
[:span {:class (stl/css-case :search-box true
|
||||
:has-children (some? children))}
|
||||
children
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
(: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]
|
||||
@@ -24,6 +25,7 @@
|
||||
[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]
|
||||
@@ -33,7 +35,19 @@
|
||||
(def ^:private close-icon
|
||||
(deprecated-icon/icon-xref :close (stl/css :close-icon)))
|
||||
|
||||
(defn icon-url
|
||||
(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
|
||||
"Creates an sanitizes de icon URL to display"
|
||||
[host icon]
|
||||
(dm/str host
|
||||
@@ -94,48 +108,49 @@
|
||||
::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*)
|
||||
|
||||
fetching-manifest? (mf/use-state false)
|
||||
input-status* (mf/use-state nil) ;; :error-url :error-manifest :success
|
||||
input-status (deref input-status*)
|
||||
|
||||
input-status* (mf/use-state nil) ;; :error-url :error-manifest :success
|
||||
input-status @input-status*
|
||||
|
||||
error-url? (= :error-url 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?)
|
||||
|
||||
user-can-edit? (:can-edit (deref refs/permissions))
|
||||
permissions (mf/deref refs/permissions)
|
||||
user-can-edit? (get permissions :can-edit)
|
||||
|
||||
handle-url-input
|
||||
fetching-manifest?
|
||||
(mf/use-state false)
|
||||
|
||||
on-url-change
|
||||
(mf/use-fn
|
||||
(fn [value]
|
||||
(reset! input-status* nil)
|
||||
(reset! plugin-url* value)))
|
||||
|
||||
handle-install-click
|
||||
on-install
|
||||
(mf/use-fn
|
||||
(mf/deps plugins-state plugin-url)
|
||||
(fn []
|
||||
(reset! fetching-manifest? true)
|
||||
(->> (dp/fetch-manifest plugin-url)
|
||||
(->> (dp/fetch-manifest (normalize-plugin-url 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
|
||||
@@ -145,7 +160,7 @@
|
||||
(reset! fetching-manifest? false)
|
||||
(reset! input-status* :error-url))))))
|
||||
|
||||
handle-open-plugin
|
||||
on-open-plugin
|
||||
(mf/use-fn
|
||||
(fn [manifest]
|
||||
(st/emit! (ptk/event ::ev/event {::ev/name "start-plugin"
|
||||
@@ -155,7 +170,7 @@
|
||||
(dp/open-plugin! manifest user-can-edit?)
|
||||
(modal/hide!)))
|
||||
|
||||
handle-remove-plugin
|
||||
on-remove-plugin
|
||||
(mf/use-fn
|
||||
(mf/deps plugins-state)
|
||||
(fn [plugin-index]
|
||||
@@ -175,14 +190,16 @@
|
||||
|
||||
[:div {:class (stl/css :modal-content)}
|
||||
[:div {:class (stl/css :top-bar)}
|
||||
[:> search-bar* {:on-change handle-url-input
|
||||
[:> search-bar* {:on-change on-url-change
|
||||
:on-submit on-install
|
||||
: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 handle-install-click} (tr "workspace.plugins.install")]]
|
||||
:on-click on-install}
|
||||
(tr "workspace.plugins.install")]]
|
||||
|
||||
(when error-url?
|
||||
[:div {:class (stl/css-case :info true :error error?)}
|
||||
@@ -220,10 +237,11 @@
|
||||
:index idx
|
||||
:manifest manifest
|
||||
:user-can-edit user-can-edit?
|
||||
:on-open-plugin handle-open-plugin
|
||||
:on-remove-plugin handle-remove-plugin}])]])]]]))
|
||||
:on-open-plugin on-open-plugin
|
||||
:on-remove-plugin on-remove-plugin}])]])]]]))
|
||||
|
||||
(mf/defc plugins-permission-list
|
||||
(mf/defc plugins-permission-list*
|
||||
{::mf/private true}
|
||||
[{:keys [permissions]}]
|
||||
[:div {:class (stl/css :permissions-list)}
|
||||
(cond
|
||||
@@ -291,36 +309,45 @@
|
||||
::mf/register-as :plugin-permissions}
|
||||
[{:keys [plugin on-accept on-close]}]
|
||||
|
||||
(let [{:keys [host permissions]} plugin
|
||||
permissions (set permissions)
|
||||
(let [host
|
||||
(:host plugin)
|
||||
|
||||
handle-accept-dialog
|
||||
permissions
|
||||
(-> plugin :permissions set)
|
||||
|
||||
on-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"
|
||||
:host host
|
||||
:permissions (->> permissions (str/join ", "))})
|
||||
(st/emit! (ev/event {::ev/name "allow-plugin-permissions"
|
||||
:host host
|
||||
:permissions (str/join ", " permissions)})
|
||||
(modal/hide))
|
||||
(when on-accept (on-accept))))
|
||||
|
||||
handle-close-dialog
|
||||
on-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"
|
||||
:host host
|
||||
:permissions (->> permissions (str/join ", "))})
|
||||
(st/emit! (ev/event {::ev/name "reject-plugin-permissions"
|
||||
:host host
|
||||
:permissions (str/join ", " permissions)})
|
||||
(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 handle-close-dialog} close-icon]
|
||||
[:button {:class (stl/css :close-btn) :on-click on-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)}
|
||||
@@ -332,13 +359,13 @@
|
||||
{:class (stl/css :cancel-button :button-expand)
|
||||
:type "button"
|
||||
:value (tr "ds.confirm-cancel")
|
||||
:on-click handle-close-dialog}]
|
||||
:on-click on-close-dialog}]
|
||||
|
||||
[:input
|
||||
{:class (stl/css :primary-button :button-expand)
|
||||
:type "button"
|
||||
:value (tr "ds.confirm-allow")
|
||||
:on-click handle-accept-dialog}]]]]]))
|
||||
:on-click on-accept-dialog}]]]]]))
|
||||
|
||||
|
||||
(mf/defc plugins-permissions-updated-dialog
|
||||
@@ -346,11 +373,15 @@
|
||||
::mf/register-as :plugin-permissions-update}
|
||||
[{:keys [plugin on-accept on-close]}]
|
||||
|
||||
(let [{:keys [host permissions]} plugin
|
||||
permissions (set permissions)
|
||||
(let [host
|
||||
(:host plugin)
|
||||
|
||||
handle-accept-dialog
|
||||
permissions
|
||||
(-> plugin :permissions set)
|
||||
|
||||
on-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"
|
||||
@@ -359,8 +390,9 @@
|
||||
(modal/hide))
|
||||
(when on-accept (on-accept))))
|
||||
|
||||
handle-close-dialog
|
||||
on-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"
|
||||
@@ -369,16 +401,20 @@
|
||||
(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 handle-close-dialog} close-icon]
|
||||
[:button {:class (stl/css :close-btn) :on-click on-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)}
|
||||
@@ -386,13 +422,13 @@
|
||||
{:class (stl/css :cancel-button :button-expand)
|
||||
:type "button"
|
||||
:value (tr "ds.confirm-cancel")
|
||||
:on-click handle-close-dialog}]
|
||||
:on-click on-close-dialog}]
|
||||
|
||||
[:input
|
||||
{:class (stl/css :primary-button :button-expand)
|
||||
:type "button"
|
||||
:value (tr "ds.confirm-allow")
|
||||
:on-click handle-accept-dialog}]]]]]))
|
||||
:on-click on-accept-dialog}]]]]]))
|
||||
|
||||
|
||||
(mf/defc plugins-try-out-dialog
|
||||
@@ -402,25 +438,31 @@
|
||||
|
||||
(let [{:keys [icon host name]} plugin
|
||||
|
||||
handle-accept-dialog
|
||||
on-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))))
|
||||
|
||||
handle-close-dialog
|
||||
on-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 handle-close-dialog} close-icon]
|
||||
[:button {:class (stl/css :close-btn) :on-click on-close-dialog} close-icon]
|
||||
[:div {:class (stl/css :modal-title)}
|
||||
[:div {:class (stl/css :plugin-icon)}
|
||||
[:img {:src (if (some? icon)
|
||||
@@ -438,10 +480,10 @@
|
||||
{:class (stl/css :cancel-button :button-expand)
|
||||
:type "button"
|
||||
:value (tr "workspace.plugins.try-out.cancel")
|
||||
:on-click handle-close-dialog}]
|
||||
:on-click on-close-dialog}]
|
||||
|
||||
[:input
|
||||
{:class (stl/css :primary-button :button-expand)
|
||||
:type "button"
|
||||
:value (tr "workspace.plugins.try-out.try")
|
||||
:on-click handle-accept-dialog}]]]]]))
|
||||
:on-click on-accept-dialog}]]]]]))
|
||||
|
||||
@@ -111,11 +111,6 @@
|
||||
:modifier modifier
|
||||
:zoom zoom}]))))
|
||||
|
||||
(defn- show-outline?
|
||||
[shape]
|
||||
(and (not (:hidden shape))
|
||||
(not (:blocked shape))))
|
||||
|
||||
(mf/defc shape-outlines
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
@@ -133,8 +128,7 @@
|
||||
|
||||
shapes (-> #{}
|
||||
(into (comp (remove edition?)
|
||||
(keep lookup)
|
||||
(filter show-outline?))
|
||||
(keep lookup))
|
||||
(set/union selected hover))
|
||||
(into (comp (remove edition?)
|
||||
(keep lookup))
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
[app.main.data.workspace.media :as dwm]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.wasm-text :as dwwt]
|
||||
[app.main.features :as features]
|
||||
[app.main.fonts :refer [fetch-font-css]]
|
||||
[app.main.router :as rt]
|
||||
[app.main.store :as st]
|
||||
@@ -365,8 +366,10 @@
|
||||
(cb/add-object shape))]
|
||||
|
||||
(st/emit! (ch/commit-changes changes)
|
||||
(se/event plugin-id "create-shape" :type :text)
|
||||
(dwwt/resize-wasm-text-debounce (:id shape)))
|
||||
(se/event plugin-id "create-shape" :type :text))
|
||||
|
||||
(when (features/active-feature? @st/state "render-wasm/v1")
|
||||
(st/emit! (dwwt/resize-wasm-text-debounce (:id shape))))
|
||||
|
||||
(shape/shape-proxy plugin-id (:id shape)))))
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
(d/without-nils
|
||||
{:plugin-id plugin-id
|
||||
:url (str plugin-url)
|
||||
:version vers
|
||||
:name name
|
||||
:description desc
|
||||
:host origin
|
||||
|
||||
@@ -1305,7 +1305,8 @@
|
||||
tokens)))}
|
||||
|
||||
:applyToken
|
||||
{:schema [:tuple
|
||||
{:enumerable false
|
||||
:schema [:tuple
|
||||
[:fn token-proxy?]
|
||||
[:maybe [:set [:and ::sm/keyword [:fn cto/token-attr?]]]]]
|
||||
:fn (fn [token attrs]
|
||||
|
||||
@@ -144,14 +144,16 @@
|
||||
(st/emit! (dwtl/delete-token set-id id)))
|
||||
|
||||
:applyToShapes
|
||||
{:schema [:tuple
|
||||
{:enumerable false
|
||||
:schema [:tuple
|
||||
[:vector [:fn shape-proxy?]]
|
||||
[:maybe [:set [:and ::sm/keyword [:fn cto/token-attr?]]]]]
|
||||
:fn (fn [shapes attrs]
|
||||
(apply-token-to-shapes file-id set-id id (map #(obj/get % "$id") shapes) attrs))}
|
||||
|
||||
:applyToSelected
|
||||
{:schema [:tuple [:maybe [:set [:and ::sm/keyword [:fn cto/token-attr?]]]]]
|
||||
{:enumerable false
|
||||
:schema [:tuple [:maybe [:set [:and ::sm/keyword [:fn cto/token-attr?]]]]]
|
||||
:fn (fn [attrs]
|
||||
(let [selected (get-in @st/state [:workspace-local :selected])]
|
||||
(apply-token-to-shapes file-id set-id id selected attrs)))}))
|
||||
@@ -236,14 +238,16 @@
|
||||
(apply array))))}
|
||||
|
||||
:getTokenById
|
||||
{:schema [:tuple ::sm/uuid]
|
||||
{:enumerable false
|
||||
:schema [:tuple ::sm/uuid]
|
||||
:fn (fn [token-id]
|
||||
(let [token (u/locate-token file-id id token-id)]
|
||||
(when (some? token)
|
||||
(token-proxy plugin-id file-id id token-id))))}
|
||||
|
||||
:addToken
|
||||
{:schema (fn [args]
|
||||
{:enumerable false
|
||||
:schema (fn [args]
|
||||
[:tuple (-> (cfo/make-token-schema
|
||||
(-> (u/locate-tokens-lib file-id) (ctob/get-tokens id))
|
||||
(cto/dtcg-token-type->token-type (-> args (first) (get "type"))))
|
||||
@@ -353,13 +357,15 @@
|
||||
{:this true :get (fn [_])}
|
||||
|
||||
:addSet
|
||||
{:schema [:tuple [:fn token-set-proxy?]]
|
||||
{:enumerable false
|
||||
:schema [:tuple [:fn token-set-proxy?]]
|
||||
:fn (fn [token-set]
|
||||
(let [theme (u/locate-token-theme file-id id)]
|
||||
(st/emit! (dwtl/update-token-theme id (ctob/enable-set theme (obj/get token-set :name))))))}
|
||||
|
||||
:removeSet
|
||||
{:schema [:tuple [:fn token-set-proxy?]]
|
||||
{:enumerable false
|
||||
:schema [:tuple [:fn token-set-proxy?]]
|
||||
:fn (fn [token-set]
|
||||
(let [theme (u/locate-token-theme file-id id)]
|
||||
(st/emit! (dwtl/update-token-theme id (ctob/disable-set theme (obj/get token-set :name))))))}
|
||||
@@ -406,7 +412,8 @@
|
||||
(apply array (map #(token-set-proxy plugin-id file-id (ctob/get-id %)) sets))))}
|
||||
|
||||
:addTheme
|
||||
{:schema (fn [attrs]
|
||||
{:enumerable false
|
||||
:schema (fn [attrs]
|
||||
[:tuple (-> (sm/schema (cfo/make-token-theme-schema
|
||||
(u/locate-tokens-lib file-id)
|
||||
(or (obj/get attrs "group") "")
|
||||
@@ -419,7 +426,8 @@
|
||||
(token-theme-proxy plugin-id file-id (:id theme))))}
|
||||
|
||||
:addSet
|
||||
{:schema [:tuple (-> (sm/schema (cfo/make-token-set-schema
|
||||
{:enumerable false
|
||||
:schema [:tuple (-> (sm/schema (cfo/make-token-set-schema
|
||||
(u/locate-tokens-lib file-id)
|
||||
nil))
|
||||
(sm/dissoc-key :id))] ;; We don't allow plugins to set the id
|
||||
@@ -431,14 +439,16 @@
|
||||
(token-set-proxy plugin-id file-id (ctob/get-id set))))}
|
||||
|
||||
:getThemeById
|
||||
{:schema [:tuple ::sm/uuid]
|
||||
{:enumerable false
|
||||
:schema [:tuple ::sm/uuid]
|
||||
:fn (fn [theme-id]
|
||||
(let [theme (u/locate-token-theme file-id theme-id)]
|
||||
(when (some? theme)
|
||||
(token-theme-proxy plugin-id file-id theme-id))))}
|
||||
|
||||
:getSetById
|
||||
{:schema [:tuple ::sm/uuid]
|
||||
{:enumerable false
|
||||
:schema [:tuple ::sm/uuid]
|
||||
:fn (fn [set-id]
|
||||
(let [set (u/locate-token-set file-id set-id)]
|
||||
(when (some? set)
|
||||
|
||||
@@ -12,7 +12,10 @@
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:application",
|
||||
"options": {
|
||||
"outputPath": "dist/apps/contrast-plugin",
|
||||
"outputPath": {
|
||||
"base": "dist/apps/contrast-plugin",
|
||||
"browser": "",
|
||||
},
|
||||
"index": "apps/contrast-plugin/src/index.html",
|
||||
"browser": "apps/contrast-plugin/src/main.ts",
|
||||
"polyfills": ["zone.js"],
|
||||
@@ -20,6 +23,7 @@
|
||||
"assets": [
|
||||
"apps/contrast-plugin/src/_headers",
|
||||
"apps/contrast-plugin/src/favicon.ico",
|
||||
"apps/contrast-plugin/src/manifest.json",
|
||||
"apps/contrast-plugin/src/assets"
|
||||
],
|
||||
"styles": [
|
||||
@@ -218,7 +222,10 @@
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:application",
|
||||
"options": {
|
||||
"outputPath": "dist/apps/table-plugin",
|
||||
"outputPath": {
|
||||
"base": "dist/apps/table-plugin",
|
||||
"browser": ""
|
||||
},
|
||||
"index": "apps/table-plugin/src/index.html",
|
||||
"browser": "apps/table-plugin/src/main.ts",
|
||||
"polyfills": ["zone.js"],
|
||||
@@ -226,6 +233,7 @@
|
||||
"assets": [
|
||||
"apps/table-plugin/src/_headers",
|
||||
"apps/table-plugin/src/favicon.ico",
|
||||
"apps/table-plugin/src/manifest.json",
|
||||
"apps/table-plugin/src/assets"
|
||||
],
|
||||
"styles": [
|
||||
@@ -356,7 +364,10 @@
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:application",
|
||||
"options": {
|
||||
"outputPath": "dist/apps/colors-to-tokens-plugin",
|
||||
"outputPath": {
|
||||
"base": "dist/apps/colors-to-tokens-plugin",
|
||||
"browser": ""
|
||||
},
|
||||
"index": "apps/colors-to-tokens-plugin/src/index.html",
|
||||
"browser": "apps/colors-to-tokens-plugin/src/main.ts",
|
||||
"polyfills": ["zone.js"],
|
||||
@@ -364,6 +375,7 @@
|
||||
"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": [
|
||||
|
||||
@@ -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",
|
||||
"build:plugin:watch": "node ../../tools/scripts/build-plugin.mjs --plugin=colors-to-tokens-plugin --watch",
|
||||
"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 build:plugin:watch\" \"pnpm run serve\"",
|
||||
"init": "concurrently --kill-others --names plugin,serve \"pnpm run watch\" \"pnpm run serve\"",
|
||||
"lint": "eslint .",
|
||||
"test": "vitest"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { ApplicationConfig } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { provideRouter, withHashLocation } from '@angular/router';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideRouter([])],
|
||||
};
|
||||
providers: [
|
||||
provideRouter([], withHashLocation())
|
||||
],
|
||||
};
|
||||
@@ -3,7 +3,6 @@
|
||||
<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>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"name": "Colors to Tokens",
|
||||
"description": "Generate a design tokens file from a list of colors",
|
||||
"code": "/assets/plugin.js",
|
||||
"icon": "/assets/icon.png",
|
||||
"version": 2,
|
||||
"code": "assets/plugin.js",
|
||||
"icon": "assets/icon.png",
|
||||
"permissions": ["content:read", "library:read", "allow:downloads"]
|
||||
}
|
||||
@@ -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",
|
||||
"build:plugin:watch": "node ../../tools/scripts/build-plugin.mjs --plugin=contrast-plugin --watch",
|
||||
"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 build:plugin:watch\" \"pnpm run serve\"",
|
||||
"init": "concurrently --kill-others --names plugin,serve \"pnpm run watch\" \"pnpm run serve\"",
|
||||
"lint": "eslint .",
|
||||
"test": "vitest"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ApplicationConfig } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { provideRouter, withHashLocation } from '@angular/router';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideRouter([])],
|
||||
providers: [provideRouter([], withHashLocation())],
|
||||
};
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>contrast-plugin</title>
|
||||
<base href="/" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"name": "Contrast",
|
||||
"description": "Measure contrast plugin",
|
||||
"code": "/assets/plugin.js",
|
||||
"icon": "/assets/icon.png",
|
||||
"version": 2,
|
||||
"code": "assets/plugin.js",
|
||||
"icon": "assets/icon.png",
|
||||
"permissions": ["content:read"]
|
||||
}
|
||||
@@ -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",
|
||||
"build:plugin:watch": "node ../../tools/scripts/build-plugin.mjs --plugin=table-plugin --watch",
|
||||
"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 build:plugin:watch\" \"pnpm run serve\"",
|
||||
"init": "concurrently --kill-others --names plugin,serve \"pnpm run watch\" \"pnpm run serve\"",
|
||||
"lint": "eslint .",
|
||||
"test": "vitest"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ApplicationConfig } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { provideRouter, withHashLocation } from '@angular/router';
|
||||
import { appRoutes } from './app.routes';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideRouter(appRoutes)],
|
||||
providers: [provideRouter(appRoutes, withHashLocation())],
|
||||
};
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<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>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"name": "Table plugin",
|
||||
"description": "Table plugin to import or create tables",
|
||||
"code": "/assets/plugin.js",
|
||||
"icon": "/assets/icon.png",
|
||||
"version": 2,
|
||||
"code": "assets/plugin.js",
|
||||
"icon": "assets/icon.png",
|
||||
"permissions": ["content:read", "content:write"]
|
||||
}
|
||||
6
plugins/apps/table-plugin/vite.config.ts
Normal file
6
plugins/apps/table-plugin/vite.config.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
/// <reference types="vitest/config" />
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
root: "./"
|
||||
});
|
||||
@@ -6,8 +6,8 @@
|
||||
"ses": "^1.1.0",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"module": "./index.mjs",
|
||||
"typings": "./index.d.ts",
|
||||
"module": "./dist/index.js",
|
||||
"typings": "./dist/index.d.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
|
||||
@@ -6,6 +6,7 @@ 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([
|
||||
|
||||
@@ -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()
|
||||
});
|
||||
|
||||
@@ -1,8 +1,28 @@
|
||||
import { Manifest } from './models/manifest.model.js';
|
||||
import { manifestSchema } from './models/manifest.schema.js';
|
||||
|
||||
export function getValidUrl(host: string, path: string): string {
|
||||
return new URL(path, host).toString();
|
||||
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 loadManifest(url: string): Promise<Manifest> {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, it, vi, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { createPluginManager } from './plugin-manager';
|
||||
import { loadManifestCode, getValidUrl } from './parse-manifest.js';
|
||||
import { loadManifestCode, getValidUrl, prepareUrl } 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,6 +9,7 @@ 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', () => ({
|
||||
@@ -71,7 +72,8 @@ describe('createPluginManager', () => {
|
||||
vi.mocked(loadManifestCode).mockResolvedValue(
|
||||
'console.log("Plugin loaded");',
|
||||
);
|
||||
vi.mocked(getValidUrl).mockReturnValue('https://example.com/plugin');
|
||||
vi.mocked(getValidUrl).mockReturnValue(new URL('https://example.com/plugin'));
|
||||
vi.mocked(prepareUrl).mockReturnValue('https://example.com/plugin');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -110,7 +112,7 @@ describe('createPluginManager', () => {
|
||||
height: 300,
|
||||
});
|
||||
|
||||
expect(getValidUrl).toHaveBeenCalledWith(manifest.host, '/test-url');
|
||||
expect(prepareUrl).toHaveBeenCalledWith(manifest, '/test-url', {theme: "light"});
|
||||
expect(openUIApi).toHaveBeenCalledWith(
|
||||
'Test Modal',
|
||||
'https://example.com/plugin',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Context, Theme } from '@penpot/plugin-types';
|
||||
|
||||
import { getValidUrl, loadManifestCode } from './parse-manifest.js';
|
||||
import { prepareUrl, 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,6 +8,7 @@ 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,
|
||||
@@ -80,9 +81,8 @@ export async function createPluginManager(
|
||||
};
|
||||
|
||||
const openModal = (name: string, url: string, options?: OpenUIOptions) => {
|
||||
const theme = context.theme as 'light' | 'dark';
|
||||
|
||||
const modalUrl = getValidUrl(manifest.host, url);
|
||||
const theme = context.theme as Theme;
|
||||
const modalUrl = prepareUrl(manifest, url, {theme});
|
||||
|
||||
if (modal?.getAttribute('iframe-src') === modalUrl) {
|
||||
return;
|
||||
|
||||
@@ -35,7 +35,7 @@ export default defineConfig({
|
||||
// Configuration for building your library.
|
||||
// See: https://vitejs.dev/guide/build.html#library-mode
|
||||
build: {
|
||||
outDir: '../../dist/plugins-runtime',
|
||||
outDir: './dist/',
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
|
||||
Reference in New Issue
Block a user