mirror of
https://github.com/penpot/penpot.git
synced 2025-02-14 19:19:09 -05:00
✨ New plugins permissions dialog
This commit is contained in:
parent
fbce59e81f
commit
741bf3b666
15 changed files with 291 additions and 84 deletions
1
frontend/resources/images/icons/oauth-1.svg
Normal file
1
frontend/resources/images/icons/oauth-1.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path d="M7.32 2.25H4.417A2.168 2.168 0 0 0 2.25 4.418v2.61M7.32 21.75H4.417c-1.197 0-2.167-1.263-2.167-2.46v-2.61m19.5-9.36V4.418a2.168 2.168 0 0 0-2.168-2.168H16.68m5.07 14.43v2.903a2.168 2.168 0 0 1-2.168 2.167H16.68M15.496 7.5H8.499a1.001 1.001 0 0 0-1.001 1.001V15.5a1 1 0 0 0 1.001 1h6.997a1 1 0 0 0 1.001-1V8.501A1 1 0 0 0 15.496 7.5Z" style="stroke-width: 1; stroke-linecap: round;"/></svg>
|
After Width: | Height: | Size: 462 B |
1
frontend/resources/images/icons/oauth-2.svg
Normal file
1
frontend/resources/images/icons/oauth-2.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path d="M20.888 22v-2.222a4.444 4.444 0 0 0-4.443-4.444h-8.89a4.444 4.444 0 0 0-4.443 4.444V22M12 10.889a4.445 4.445 0 1 0 .172-8.888A4.445 4.445 0 0 0 12 10.889Z" style="stroke-width: 1; stroke-opacity: 1; stroke-linecap: round;"/></svg>
|
After Width: | Height: | Size: 303 B |
1
frontend/resources/images/icons/oauth-3.svg
Normal file
1
frontend/resources/images/icons/oauth-3.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path d="M6.5 2H20v20H6.5a2.5 2.5 0 0 1-2.499-2.436L4 19.5v-15A2.5 2.5 0 0 1 6.5 2ZM20 17h0ZH6.5a2.5 2.5 0 0 0-2.499 2.436" style="stroke-width: 1; stroke-linecap: round;" /></svg>
|
After Width: | Height: | Size: 244 B |
1
frontend/resources/images/icons/puzzle.svg
Normal file
1
frontend/resources/images/icons/puzzle.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19.439 7.85c-.049.322.059.648.289.878l1.568 1.568c.47.47.706 1.087.706 1.704s-.235 1.233-.706 1.704l-1.611 1.611a.98.98 0 0 1-.837.276c-.47-.07-.802-.48-.968-.925a2.501 2.501 0 1 0-3.214 3.214c.446.166.855.497.925.968a.979.979 0 0 1-.276.837l-1.61 1.61a2.404 2.404 0 0 1-1.705.707 2.402 2.402 0 0 1-1.704-.706l-1.568-1.568a1.026 1.026 0 0 0-.877-.29c-.493.074-.84.504-1.02.968a2.5 2.5 0 1 1-3.237-3.237c.464-.18.894-.527.967-1.02a1.026 1.026 0 0 0-.289-.877l-1.568-1.568A2.402 2.402 0 0 1 1.998 12c0-.617.236-1.234.706-1.704L4.23 8.77c.24-.24.581-.353.917-.303.515.077.877.528 1.073 1.01a2.5 2.5 0 1 0 3.259-3.259c-.482-.196-.933-.558-1.01-1.073-.05-.336.062-.676.303-.917l1.525-1.525A2.402 2.402 0 0 1 12 1.998c.617 0 1.234.236 1.704.706l1.568 1.568c.23.23.556.338.877.29.493-.074.84-.504 1.02-.968a2.5 2.5 0 1 1 3.237 3.237c-.464.18-.894.527-.967 1.02Z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
After Width: | Height: | Size: 1,021 B |
|
@ -110,6 +110,7 @@
|
||||||
(def privacy-policy-uri (obj/get global "penpotPrivacyPolicyURI" "https://penpot.app/privacy"))
|
(def privacy-policy-uri (obj/get global "penpotPrivacyPolicyURI" "https://penpot.app/privacy"))
|
||||||
(def flex-help-uri (obj/get global "penpotGridHelpURI" "https://help.penpot.app/user-guide/flexible-layouts/"))
|
(def flex-help-uri (obj/get global "penpotGridHelpURI" "https://help.penpot.app/user-guide/flexible-layouts/"))
|
||||||
(def grid-help-uri (obj/get global "penpotGridHelpURI" "https://help.penpot.app/user-guide/flexible-layouts/"))
|
(def grid-help-uri (obj/get global "penpotGridHelpURI" "https://help.penpot.app/user-guide/flexible-layouts/"))
|
||||||
|
(def plugins-list-uri (obj/get global "penpotPluginsListUri" "https://penpot-docs-plugins.netlify.app/technical-guide/plugins/getting-started/#examples"))
|
||||||
|
|
||||||
(defn- normalize-uri
|
(defn- normalize-uri
|
||||||
[uri-str]
|
[uri-str]
|
||||||
|
|
|
@ -178,6 +178,9 @@
|
||||||
(def ^:icon msg-success (icon-xref :msg-success))
|
(def ^:icon msg-success (icon-xref :msg-success))
|
||||||
(def ^:icon msg-warning (icon-xref :msg-warning))
|
(def ^:icon msg-warning (icon-xref :msg-warning))
|
||||||
(def ^:icon open-link (icon-xref :open-link))
|
(def ^:icon open-link (icon-xref :open-link))
|
||||||
|
(def ^:icon oauth-1 (icon-xref :oauth-1))
|
||||||
|
(def ^:icon oauth-2 (icon-xref :oauth-2))
|
||||||
|
(def ^:icon oauth-3 (icon-xref :oauth-3))
|
||||||
(def ^:icon padding-bottom (icon-xref :padding-bottom))
|
(def ^:icon padding-bottom (icon-xref :padding-bottom))
|
||||||
(def ^:icon padding-extended (icon-xref :padding-extended))
|
(def ^:icon padding-extended (icon-xref :padding-extended))
|
||||||
(def ^:icon padding-left (icon-xref :padding-left))
|
(def ^:icon padding-left (icon-xref :padding-left))
|
||||||
|
@ -190,6 +193,7 @@
|
||||||
(def ^:icon picker (icon-xref :picker))
|
(def ^:icon picker (icon-xref :picker))
|
||||||
(def ^:icon pin (icon-xref :pin))
|
(def ^:icon pin (icon-xref :pin))
|
||||||
(def ^:icon play (icon-xref :play))
|
(def ^:icon play (icon-xref :play))
|
||||||
|
(def ^:icon puzzle (icon-xref :puzzle))
|
||||||
(def ^:icon rectangle (icon-xref :rectangle))
|
(def ^:icon rectangle (icon-xref :rectangle))
|
||||||
(def ^:icon reload (icon-xref :reload))
|
(def ^:icon reload (icon-xref :reload))
|
||||||
(def ^:icon remove-icon (icon-xref :remove))
|
(def ^:icon remove-icon (icon-xref :remove))
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
[app.main.ui.hooks.resize :as r]
|
[app.main.ui.hooks.resize :as r]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.main.ui.workspace.plugins :as uwp]
|
[app.main.ui.workspace.plugins :as uwp]
|
||||||
|
[app.plugins :as plugins]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[app.util.keyboard :as kbd]
|
[app.util.keyboard :as kbd]
|
||||||
|
@ -608,7 +609,7 @@
|
||||||
::mf/wrap [mf/memo]}
|
::mf/wrap [mf/memo]}
|
||||||
[{:keys [open-plugins on-close]}]
|
[{:keys [open-plugins on-close]}]
|
||||||
(when (features/active-feature? @st/state "plugins/runtime")
|
(when (features/active-feature? @st/state "plugins/runtime")
|
||||||
(let [plugins (uwp/load-from-store)]
|
(let [plugins (plugins/load-from-store)]
|
||||||
[:& dropdown-menu {:show true
|
[:& dropdown-menu {:show true
|
||||||
:list-class (stl/css-case :sub-menu true :plugins true)
|
:list-class (stl/css-case :sub-menu true :plugins true)
|
||||||
:on-close on-close}
|
:on-close on-close}
|
||||||
|
|
|
@ -9,22 +9,23 @@
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.uuid :as uuid]
|
[app.config :as cf]
|
||||||
[app.main.data.modal :as modal]
|
[app.main.data.modal :as modal]
|
||||||
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.search-bar :refer [search-bar]]
|
[app.main.ui.components.search-bar :refer [search-bar]]
|
||||||
[app.main.ui.components.title-bar :refer [title-bar]]
|
[app.main.ui.components.title-bar :refer [title-bar]]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
|
[app.plugins :as plugins]
|
||||||
[app.util.avatars :as avatars]
|
[app.util.avatars :as avatars]
|
||||||
|
[app.util.dom :as dom]
|
||||||
[app.util.http :as http]
|
[app.util.http :as http]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[app.util.object :as obj]
|
|
||||||
[beicon.v2.core :as rx]
|
[beicon.v2.core :as rx]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(def ^:private close-icon
|
(def ^:private close-icon
|
||||||
(i/icon-xref :close (stl/css :close-icon)))
|
(i/icon-xref :close (stl/css :close-icon)))
|
||||||
|
|
||||||
|
|
||||||
(mf/defc plugin-entry
|
(mf/defc plugin-entry
|
||||||
[{:keys [index manifest on-open-plugin on-remove-plugin]}]
|
[{:keys [index manifest on-open-plugin on-remove-plugin]}]
|
||||||
|
|
||||||
|
@ -55,20 +56,6 @@
|
||||||
[:button {:class (stl/css :trash-button)
|
[:button {:class (stl/css :trash-button)
|
||||||
:on-click handle-delete-click} i/delete]]))
|
:on-click handle-delete-click} i/delete]]))
|
||||||
|
|
||||||
(defn load-from-store
|
|
||||||
[]
|
|
||||||
(let [ls (.-localStorage js/window)
|
|
||||||
plugins-val (.getItem ls "plugins")]
|
|
||||||
(when plugins-val
|
|
||||||
(let [plugins-js (.parse js/JSON plugins-val)]
|
|
||||||
(js->clj plugins-js {:keywordize-keys true})))))
|
|
||||||
|
|
||||||
(defn save-to-store
|
|
||||||
[plugins]
|
|
||||||
(let [ls (.-localStorage js/window)
|
|
||||||
plugins-js (clj->js plugins)
|
|
||||||
plugins-val (.stringify js/JSON plugins-js)]
|
|
||||||
(.setItem ls "plugins" plugins-val)))
|
|
||||||
|
|
||||||
(defn open-plugin!
|
(defn open-plugin!
|
||||||
[{:keys [plugin-id name description host code icon permissions]}]
|
[{:keys [plugin-id name description host code icon permissions]}]
|
||||||
|
@ -123,27 +110,20 @@
|
||||||
(rx/map :body)
|
(rx/map :body)
|
||||||
(rx/subs!
|
(rx/subs!
|
||||||
(fn [body]
|
(fn [body]
|
||||||
(let [name (obj/get body "name")
|
(let [plugin (plugins/parser-manifest plugin-url body)
|
||||||
desc (obj/get body "description")
|
new-state (vec (conj (seq plugins-state) plugin))]
|
||||||
code (obj/get body "code")
|
|
||||||
icon (obj/get body "icon")
|
|
||||||
permissions (obj/get body "permissions")
|
|
||||||
origin (obj/get (js/URL. plugin-url) "origin")
|
|
||||||
plugin-id (str (uuid/next))
|
|
||||||
|
|
||||||
new-state
|
|
||||||
(conj plugins-state
|
|
||||||
{:plugin-id plugin-id
|
|
||||||
:name name
|
|
||||||
:description desc
|
|
||||||
:host origin
|
|
||||||
:code code
|
|
||||||
:icon icon
|
|
||||||
:permissions (->> permissions (mapv str))})]
|
|
||||||
(reset! input-status* :success)
|
(reset! input-status* :success)
|
||||||
(reset! plugin-url* "")
|
(reset! plugin-url* "")
|
||||||
(reset! plugins-state* new-state)
|
(reset! plugins-state* new-state)
|
||||||
(save-to-store new-state)))
|
|
||||||
|
(modal/show!
|
||||||
|
:plugin-permissions
|
||||||
|
{:plugin plugin
|
||||||
|
:on-accept
|
||||||
|
#(do
|
||||||
|
(plugins/save-to-store new-state)
|
||||||
|
(modal/show! :plugin-management {}))})))
|
||||||
(fn [_]
|
(fn [_]
|
||||||
(reset! input-status* :error-url))))))
|
(reset! input-status* :error-url))))))
|
||||||
|
|
||||||
|
@ -162,16 +142,15 @@
|
||||||
(keep-indexed (fn [idx item]
|
(keep-indexed (fn [idx item]
|
||||||
(when (not= idx plugin-index) item)))
|
(when (not= idx plugin-index) item)))
|
||||||
plugins-state)]
|
plugins-state)]
|
||||||
|
|
||||||
(reset! plugins-state* new-state)
|
(reset! plugins-state* new-state)
|
||||||
(save-to-store new-state))))]
|
(plugins/save-to-store new-state))))]
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(fn []
|
(fn []
|
||||||
(reset! plugins-state* (d/nilv (load-from-store) []))))
|
(reset! plugins-state* (d/nilv (plugins/load-from-store) []))))
|
||||||
|
|
||||||
[:div {:class (stl/css :modal-overlay)}
|
[:div {:class (stl/css :modal-overlay)}
|
||||||
[:div {:class (stl/css :modal-dialog)}
|
[:div {:class (stl/css :modal-dialog :plugin-management)}
|
||||||
[:button {:class (stl/css :close-btn) :on-click handle-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.title")]
|
[:div {:class (stl/css :modal-title)} (tr "workspace.plugins.title")]
|
||||||
|
|
||||||
|
@ -183,7 +162,6 @@
|
||||||
:class (stl/css-case :input-error error?)}]
|
:class (stl/css-case :input-error error?)}]
|
||||||
|
|
||||||
[:button {:class (stl/css :primary-button)
|
[:button {:class (stl/css :primary-button)
|
||||||
:disabled (empty? plugin-url)
|
|
||||||
:on-click handle-install-click} (tr "workspace.plugins.install")]]
|
:on-click handle-install-click} (tr "workspace.plugins.install")]]
|
||||||
|
|
||||||
(when error?
|
(when error?
|
||||||
|
@ -194,9 +172,9 @@
|
||||||
|
|
||||||
(if (empty? plugins-state)
|
(if (empty? plugins-state)
|
||||||
[:div {:class (stl/css :plugins-empty)}
|
[:div {:class (stl/css :plugins-empty)}
|
||||||
[:div {:class (stl/css :plugins-empty-logo)} i/rocket]
|
[:div {:class (stl/css :plugins-empty-logo)} i/puzzle]
|
||||||
[:div {:class (stl/css :plugins-empty-text)} (tr "workspace.plugins.empty-plugins")]
|
[:div {:class (stl/css :plugins-empty-text)} (tr "workspace.plugins.empty-plugins")]
|
||||||
[:a {:class (stl/css :plugins-link) :href "#"}
|
[:a {:class (stl/css :plugins-link) :href cf/plugins-list-uri :target "_blank"}
|
||||||
(tr "workspace.plugins.plugin-list-link") i/external-link]]
|
(tr "workspace.plugins.plugin-list-link") i/external-link]]
|
||||||
|
|
||||||
[:*
|
[:*
|
||||||
|
@ -204,10 +182,84 @@
|
||||||
:title (tr "workspace.plugins.installed-plugins")}]
|
:title (tr "workspace.plugins.installed-plugins")}]
|
||||||
|
|
||||||
[:div {:class (stl/css :plugins-list)}
|
[:div {:class (stl/css :plugins-list)}
|
||||||
|
|
||||||
(for [[idx manifest] (d/enumerate plugins-state)]
|
(for [[idx manifest] (d/enumerate plugins-state)]
|
||||||
[:& plugin-entry {:key (dm/str "plugin-" idx)
|
[:& plugin-entry {:key (dm/str "plugin-" idx)
|
||||||
:index idx
|
:index idx
|
||||||
:manifest manifest
|
:manifest manifest
|
||||||
:on-open-plugin handle-open-plugin
|
:on-open-plugin handle-open-plugin
|
||||||
:on-remove-plugin handle-remove-plugin}])]])]]]))
|
:on-remove-plugin handle-remove-plugin}])]])]]]))
|
||||||
|
|
||||||
|
(mf/defc plugins-permissions-dialog
|
||||||
|
{::mf/register modal/components
|
||||||
|
::mf/register-as :plugin-permissions}
|
||||||
|
[{:keys [plugin on-accept]}]
|
||||||
|
|
||||||
|
(let [{:keys [permissions]} plugin
|
||||||
|
permissions (set permissions)
|
||||||
|
|
||||||
|
handle-accept-dialog
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [event]
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(st/emit! (modal/hide))
|
||||||
|
(on-accept)))
|
||||||
|
|
||||||
|
handle-close-dialog
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [event]
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(st/emit! (modal/hide))))]
|
||||||
|
|
||||||
|
[: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]
|
||||||
|
[:div {:class (stl/css :modal-title)} (tr "workspace.plugins.permissions.title")]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :modal-content)}
|
||||||
|
[:div {:class (stl/css :permissions-list)}
|
||||||
|
(when (contains? permissions "content:read")
|
||||||
|
[:div {:class (stl/css :permissions-list-entry)}
|
||||||
|
i/oauth-1
|
||||||
|
[:p {:class (stl/css :permissions-list-text)}
|
||||||
|
(tr "workspace.plugins.permissions.disclaimer")]])
|
||||||
|
|
||||||
|
(when (contains? permissions "content:write")
|
||||||
|
[:div {:class (stl/css :permissions-list-entry)}
|
||||||
|
i/oauth-1
|
||||||
|
[:p {:class (stl/css :permissions-list-text)}
|
||||||
|
(tr "workspace.plugins.permissions.content-read")]])
|
||||||
|
|
||||||
|
(when (contains? permissions "user:read")
|
||||||
|
[:div {:class (stl/css :permissions-list-entry)}
|
||||||
|
i/oauth-2
|
||||||
|
[:p {:class (stl/css :permissions-list-text)}
|
||||||
|
(tr "workspace.plugins.permissions.content-write")]])
|
||||||
|
|
||||||
|
(when (contains? permissions "library:read")
|
||||||
|
[:div {:class (stl/css :permissions-list-entry)}
|
||||||
|
i/oauth-3
|
||||||
|
[:p {:class (stl/css :permissions-list-text)}
|
||||||
|
(tr "workspace.plugins.permissions.user-read")]])
|
||||||
|
|
||||||
|
(when (contains? permissions "library:write")
|
||||||
|
[:div {:class (stl/css :permissions-list-entry)}
|
||||||
|
i/oauth-3
|
||||||
|
[:p {:class (stl/css :permissions-list-text)}
|
||||||
|
(tr "workspace.plugins.permissions.library-read")]])]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :permissions-disclaimer)}
|
||||||
|
(tr "workspace.plugins.permissions.library-write")]]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :modal-footer)}
|
||||||
|
[:div {:class (stl/css :action-buttons)}
|
||||||
|
[:input
|
||||||
|
{:class (stl/css :cancel-button :button-expand)
|
||||||
|
:type "button"
|
||||||
|
:value (tr "ds.confirm-cancel")
|
||||||
|
:on-click handle-close-dialog}]
|
||||||
|
|
||||||
|
[:input
|
||||||
|
{:class (stl/css :primary-button :button-expand)
|
||||||
|
:type "button"
|
||||||
|
:value (tr "ds.confirm-allow")
|
||||||
|
:on-click handle-accept-dialog}]]]]]))
|
||||||
|
|
|
@ -13,15 +13,27 @@
|
||||||
.modal-dialog {
|
.modal-dialog {
|
||||||
@extend .modal-container-base;
|
@extend .modal-container-base;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: auto 1fr;
|
grid-template-rows: auto 1fr auto;
|
||||||
width: $s-472;
|
|
||||||
max-width: $s-472;
|
&.plugin-permissions {
|
||||||
|
width: $s-412;
|
||||||
|
max-width: $s-412;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.plugin-management {
|
||||||
|
width: $s-472;
|
||||||
|
max-width: $s-472;
|
||||||
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
border-color: $db-tertiary;
|
border-color: $db-tertiary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.close-btn {
|
.close-btn {
|
||||||
@extend .modal-close-btn-base;
|
@extend .modal-close-btn-base;
|
||||||
}
|
}
|
||||||
|
@ -40,8 +52,15 @@
|
||||||
.modal-content {
|
.modal-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: $s-380;
|
|
||||||
max-height: $s-380;
|
.plugin-permissions & {
|
||||||
|
gap: $s-20;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-management & {
|
||||||
|
height: $s-380;
|
||||||
|
max-height: $s-380;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary-button {
|
.primary-button {
|
||||||
|
@ -50,6 +69,17 @@
|
||||||
padding: $s-0 $s-16;
|
padding: $s-0 $s-16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button-expand {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-button {
|
||||||
|
@extend .button-secondary;
|
||||||
|
@include headlineSmallTypography;
|
||||||
|
padding: $s-0 $s-16;
|
||||||
|
}
|
||||||
|
|
||||||
.search-icon {
|
.search-icon {
|
||||||
@include flexCenter;
|
@include flexCenter;
|
||||||
width: $s-20;
|
width: $s-20;
|
||||||
|
@ -87,7 +117,7 @@
|
||||||
.plugins-list {
|
.plugins-list {
|
||||||
padding-top: $s-20;
|
padding-top: $s-20;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: scroll;
|
overflow-y: auto;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -148,8 +178,8 @@
|
||||||
svg {
|
svg {
|
||||||
width: $s-16;
|
width: $s-16;
|
||||||
height: $s-16;
|
height: $s-16;
|
||||||
fill: $df-secondary;
|
stroke: $df-secondary;
|
||||||
stroke-width: 0;
|
fill: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,3 +220,45 @@ div.input-error {
|
||||||
fill: none;
|
fill: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.plugin-permissions {
|
||||||
|
}
|
||||||
|
|
||||||
|
.permissions-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $s-24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.permissions-list-entry {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 24px 1fr;
|
||||||
|
gap: $s-16;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: $s-24;
|
||||||
|
height: $s-24;
|
||||||
|
stroke: $da-primary;
|
||||||
|
fill: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.permissions-list-text {
|
||||||
|
@include bodySmallTypography;
|
||||||
|
margin: 0;
|
||||||
|
color: $df-secondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.permissions-disclaimer {
|
||||||
|
@include bodySmallTypography;
|
||||||
|
padding: $s-16;
|
||||||
|
background: $db-tertiary;
|
||||||
|
color: $df-secondary;
|
||||||
|
border-radius: $br-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: $s-12;
|
||||||
|
}
|
||||||
|
|
|
@ -11,11 +11,13 @@
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.media :as cm]
|
[app.common.media :as cm]
|
||||||
[app.main.data.events :as ev]
|
[app.main.data.events :as ev]
|
||||||
|
[app.main.data.modal :as modal]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.common :as dwc]
|
||||||
[app.main.data.workspace.media :as dwm]
|
[app.main.data.workspace.media :as dwm]
|
||||||
[app.main.data.workspace.path.state :as pst]
|
[app.main.data.workspace.path.state :as pst]
|
||||||
[app.main.data.workspace.shortcuts :as sc]
|
[app.main.data.workspace.shortcuts :as sc]
|
||||||
|
[app.main.features :as features]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
||||||
|
@ -192,6 +194,17 @@
|
||||||
:data-testid "path-btn"}
|
:data-testid "path-btn"}
|
||||||
i/path]]
|
i/path]]
|
||||||
|
|
||||||
|
(when (features/active-feature? @st/state "plugins/runtime")
|
||||||
|
[:li
|
||||||
|
[:button
|
||||||
|
{:title (tr "workspace.toolbar.plugins" (sc/get-tooltip :plugins))
|
||||||
|
:aria-label (tr "workspace.toolbar.plugins" (sc/get-tooltip :plugins))
|
||||||
|
:class (stl/css :main-toolbar-options-button)
|
||||||
|
:on-click #(modal/show! :plugin-management {})
|
||||||
|
:data-tool "plugins"
|
||||||
|
:data-testid "plugins-btn"}
|
||||||
|
i/puzzle]])
|
||||||
|
|
||||||
(when *assert*
|
(when *assert*
|
||||||
[:li
|
[:li
|
||||||
[:button
|
[:button
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
(ns app.plugins
|
(ns app.plugins
|
||||||
"RPC for plugins runtime."
|
"RPC for plugins runtime."
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
[app.main.features :as features]
|
[app.main.features :as features]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.plugins.api :as api]
|
[app.plugins.api :as api]
|
||||||
|
@ -33,3 +34,35 @@
|
||||||
(rx/take 1)
|
(rx/take 1)
|
||||||
(rx/tap init-plugins-runtime!)
|
(rx/tap init-plugins-runtime!)
|
||||||
(rx/ignore)))))
|
(rx/ignore)))))
|
||||||
|
|
||||||
|
(defn parser-manifest
|
||||||
|
[plugin-url ^js manifest]
|
||||||
|
(let [name (obj/get manifest "name")
|
||||||
|
desc (obj/get manifest "description")
|
||||||
|
code (obj/get manifest "code")
|
||||||
|
icon (obj/get manifest "icon")
|
||||||
|
permissions (obj/get manifest "permissions")
|
||||||
|
origin (obj/get (js/URL. plugin-url) "origin")
|
||||||
|
plugin-id (str (uuid/next))]
|
||||||
|
{:plugin-id plugin-id
|
||||||
|
:name name
|
||||||
|
:description desc
|
||||||
|
:host origin
|
||||||
|
:code code
|
||||||
|
:icon icon
|
||||||
|
:permissions (->> permissions (mapv str))}))
|
||||||
|
|
||||||
|
(defn load-from-store
|
||||||
|
[]
|
||||||
|
(let [ls (.-localStorage js/window)
|
||||||
|
plugins-val (.getItem ls "plugins")]
|
||||||
|
(when plugins-val
|
||||||
|
(let [plugins-js (.parse js/JSON plugins-val)]
|
||||||
|
(js->clj plugins-js {:keywordize-keys true})))))
|
||||||
|
|
||||||
|
(defn save-to-store
|
||||||
|
[plugins]
|
||||||
|
(let [ls (.-localStorage js/window)
|
||||||
|
plugins-js (clj->js plugins)
|
||||||
|
plugins-val (.stringify js/JSON plugins-js)]
|
||||||
|
(.setItem ls "plugins" plugins-val)))
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
(ns app.plugins.events
|
(ns app.plugins.events
|
||||||
(:require
|
(:require
|
||||||
[app.common.data.macros :as dm]
|
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.plugins.file :as file]
|
[app.plugins.file :as file]
|
||||||
[app.plugins.page :as page]
|
[app.plugins.page :as page]
|
||||||
|
@ -24,25 +23,19 @@
|
||||||
|
|
||||||
(defmethod handle-state-change "filechange"
|
(defmethod handle-state-change "filechange"
|
||||||
[_ plugin-id old-val new-val]
|
[_ plugin-id old-val new-val]
|
||||||
(let [old-file (:workspace-file old-val)
|
(let [old-file-id (:current-file-id old-val)
|
||||||
new-file (:workspace-file new-val)
|
new-file-id (:current-file-id new-val)]
|
||||||
old-data (:workspace-data old-val)
|
(if (identical? old-file-id new-file-id)
|
||||||
new-data (:workspace-data new-val)]
|
|
||||||
(if (and (identical? old-file new-file)
|
|
||||||
(identical? old-data new-data))
|
|
||||||
::not-changed
|
::not-changed
|
||||||
(file/file-proxy plugin-id (:id new-file)))))
|
(file/file-proxy plugin-id new-file-id))))
|
||||||
|
|
||||||
(defmethod handle-state-change "pagechange"
|
(defmethod handle-state-change "pagechange"
|
||||||
[_ plugin-id old-val new-val]
|
[_ plugin-id old-val new-val]
|
||||||
(let [file-id (:current-file-id new-val)
|
(let [old-page-id (:current-page-id old-val)
|
||||||
old-page-id (:current-page-id old-val)
|
new-page-id (:current-page-id new-val)]
|
||||||
new-page-id (:current-page-id new-val)
|
(if (identical? old-page-id new-page-id)
|
||||||
old-page (dm/get-in old-val [:workspace-data :pages-index old-page-id])
|
|
||||||
new-page (dm/get-in new-val [:workspace-data :pages-index new-page-id])]
|
|
||||||
(if (identical? old-page new-page)
|
|
||||||
::not-changed
|
::not-changed
|
||||||
(page/page-proxy plugin-id file-id new-page-id))))
|
(page/page-proxy plugin-id (:current-file-id new-val) new-page-id))))
|
||||||
|
|
||||||
(defmethod handle-state-change "selectionchange"
|
(defmethod handle-state-change "selectionchange"
|
||||||
[_ _ old-val new-val]
|
[_ _ old-val new-val]
|
||||||
|
@ -75,7 +68,10 @@
|
||||||
(fn [_ _ old-val new-val]
|
(fn [_ _ old-val new-val]
|
||||||
(let [result (handle-state-change type plugin-id old-val new-val)]
|
(let [result (handle-state-change type plugin-id old-val new-val)]
|
||||||
(when (not= ::not-changed result)
|
(when (not= ::not-changed result)
|
||||||
(callback result)))))
|
(try
|
||||||
|
(callback result)
|
||||||
|
(catch :default cause
|
||||||
|
(.error js/console cause)))))))
|
||||||
|
|
||||||
;; return the generated key
|
;; return the generated key
|
||||||
key))
|
key))
|
||||||
|
|
|
@ -60,23 +60,6 @@
|
||||||
(declare shape-proxy)
|
(declare shape-proxy)
|
||||||
(declare shape-proxy?)
|
(declare shape-proxy?)
|
||||||
|
|
||||||
#_(defn parse-command
|
|
||||||
[entry]
|
|
||||||
(update entry
|
|
||||||
:command
|
|
||||||
#(case %
|
|
||||||
"M" :move-to
|
|
||||||
"Z" :close-path
|
|
||||||
"L" :line-to
|
|
||||||
"H" :line-to-horizontal
|
|
||||||
"V" :line-to-vertical
|
|
||||||
"C" :curve-to
|
|
||||||
"S" :smooth-curve-to
|
|
||||||
"Q" :quadratic-bezier-curve-to
|
|
||||||
"T" :smooth-quadratic-bezier-curve-to
|
|
||||||
"A" :elliptical-arc
|
|
||||||
(keyword %))))
|
|
||||||
|
|
||||||
(defn- shadow-defaults
|
(defn- shadow-defaults
|
||||||
[shadow]
|
[shadow]
|
||||||
(d/patch-object
|
(d/patch-object
|
||||||
|
|
|
@ -847,6 +847,9 @@ msgstr "Cancel"
|
||||||
msgid "ds.confirm-ok"
|
msgid "ds.confirm-ok"
|
||||||
msgstr "Ok"
|
msgstr "Ok"
|
||||||
|
|
||||||
|
msgid "ds.confirm-allow"
|
||||||
|
msgstr "Allow"
|
||||||
|
|
||||||
#: src/app/main/ui/confirm.cljs, src/app/main/ui/confirm.cljs
|
#: src/app/main/ui/confirm.cljs, src/app/main/ui/confirm.cljs
|
||||||
msgid "ds.confirm-title"
|
msgid "ds.confirm-title"
|
||||||
msgstr "Are you sure?"
|
msgstr "Are you sure?"
|
||||||
|
@ -5290,3 +5293,24 @@ msgstr "Plugins manager"
|
||||||
|
|
||||||
msgid "workspace.plugins.plugin-list-link"
|
msgid "workspace.plugins.plugin-list-link"
|
||||||
msgstr "Plugins List"
|
msgstr "Plugins List"
|
||||||
|
|
||||||
|
msgid "workspace.plugins.permissions.title"
|
||||||
|
msgstr "THIS PLUGIN WANTS ACCESS TO:"
|
||||||
|
|
||||||
|
msgid "workspace.plugins.permissions.disclaimer"
|
||||||
|
msgstr "Note that this plugin has been created by an external party."
|
||||||
|
|
||||||
|
msgid "workspace.plugins.permissions.content-read"
|
||||||
|
msgstr "Read the content of files that users have access to."
|
||||||
|
|
||||||
|
msgid "workspace.plugins.permissions.content-write"
|
||||||
|
msgstr "Read and modify the content of files that users have access to."
|
||||||
|
|
||||||
|
msgid "workspace.plugins.permissions.user-read"
|
||||||
|
msgstr "Read the profile information of the current user."
|
||||||
|
|
||||||
|
msgid "workspace.plugins.permissions.library-read"
|
||||||
|
msgstr "Read your libraries and assets."
|
||||||
|
|
||||||
|
msgid "workspace.plugins.permissions.library-write"
|
||||||
|
msgstr "Read and modify your libraries and assets."
|
||||||
|
|
|
@ -869,6 +869,9 @@ msgstr "Cancelar"
|
||||||
msgid "ds.confirm-ok"
|
msgid "ds.confirm-ok"
|
||||||
msgstr "Ok"
|
msgstr "Ok"
|
||||||
|
|
||||||
|
msgid "ds.confirm-allow"
|
||||||
|
msgstr "Permitir"
|
||||||
|
|
||||||
#: src/app/main/ui/confirm.cljs, src/app/main/ui/confirm.cljs
|
#: src/app/main/ui/confirm.cljs, src/app/main/ui/confirm.cljs
|
||||||
msgid "ds.confirm-title"
|
msgid "ds.confirm-title"
|
||||||
msgstr "¿Está Seguro?"
|
msgstr "¿Está Seguro?"
|
||||||
|
@ -5394,3 +5397,24 @@ msgstr "Gestor de extensiones"
|
||||||
|
|
||||||
msgid "workspace.plugins.plugin-list-link"
|
msgid "workspace.plugins.plugin-list-link"
|
||||||
msgstr "Lista de extensiones"
|
msgstr "Lista de extensiones"
|
||||||
|
|
||||||
|
msgid "workspace.plugins.permissions.title"
|
||||||
|
msgstr "LA EXTENSIÓN SOLICITA PERMISO PARA ACCEDER:"
|
||||||
|
|
||||||
|
msgid "workspace.plugins.permissions.disclaimer"
|
||||||
|
msgstr "Tenga en cuenta que esta extensión ha sido desarrollada por terceros."
|
||||||
|
|
||||||
|
msgid "workspace.plugins.permissions.content-read"
|
||||||
|
msgstr "Leer el contenido de sus archivos."
|
||||||
|
|
||||||
|
msgid "workspace.plugins.permissions.content-write"
|
||||||
|
msgstr "Leer y modificar el contenido de sus archivos."
|
||||||
|
|
||||||
|
msgid "workspace.plugins.permissions.user-read"
|
||||||
|
msgstr "Leer la información del usuario actual."
|
||||||
|
|
||||||
|
msgid "workspace.plugins.permissions.library-read"
|
||||||
|
msgstr "Leer la información de sus bibliotecas y recursos."
|
||||||
|
|
||||||
|
msgid "workspace.plugins.permissions.library-write"
|
||||||
|
msgstr "Leer y modificar la información de sus bibliotecas y recursos."
|
||||||
|
|
Loading…
Add table
Reference in a new issue