mirror of
https://github.com/penpot/penpot.git
synced 2026-02-06 04:32:49 -05:00
Compare commits
2 Commits
fix-ipv6-f
...
hiru-plugi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ddfb5cce5 | ||
|
|
6e48e73b40 |
@@ -39,12 +39,12 @@
|
||||
(into {})))
|
||||
|
||||
(defn remove-attributes-for-token
|
||||
"Removes applied tokens with `token-name` for the given `attributes` set from `applied-tokens`."
|
||||
[attributes token-name applied-tokens]
|
||||
"Removes applied tokens with `token-id` for the given `attributes` set from `applied-tokens`."
|
||||
[attributes token applied-tokens]
|
||||
(let [attr? (set attributes)]
|
||||
(->> (remove (fn [[k v]]
|
||||
(and (attr? k)
|
||||
(= v token-name)))
|
||||
(= v (or (token-identifier token) token))))
|
||||
applied-tokens)
|
||||
(into {}))))
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
resolver $PENPOT_INTERNAL_RESOLVER valid=10s;
|
||||
resolver $PENPOT_INTERNAL_RESOLVER ipv6=off valid=10s;
|
||||
|
||||
@@ -73,7 +73,6 @@ http {
|
||||
|
||||
server {
|
||||
listen 8080 default_server;
|
||||
listen [::]:8080 default_server;
|
||||
server_name _;
|
||||
|
||||
client_max_body_size $PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE;
|
||||
|
||||
@@ -831,102 +831,15 @@ test.describe("Tokens: Apply token", () => {
|
||||
});
|
||||
await detachButton.click();
|
||||
await expect(marginPillXL).not.toBeVisible();
|
||||
const horizontalMarginInput = layoutItemSectionSidebar.getByText(
|
||||
"Horizontal marginOpen token",
|
||||
);
|
||||
const horizontalMarginInput = layoutItemSectionSidebar.getByText('Horizontal marginOpen token');
|
||||
await expect(horizontalMarginInput).toBeVisible();
|
||||
|
||||
const tokenDropdown = horizontalMarginInput.getByRole("button", {
|
||||
name: "Open token list",
|
||||
});
|
||||
const tokenDropdown = horizontalMarginInput.getByRole('button', { name: 'Open token list' });
|
||||
await tokenDropdown.click();
|
||||
|
||||
await expect(dimensionTokenOptionXl).toBeVisible();
|
||||
await dimensionTokenOptionXl.click();
|
||||
|
||||
|
||||
await expect(marginPillXL).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Tokens: Detach token", () => {
|
||||
test("User applies border-radius token to a shape from sidebar", async ({
|
||||
page,
|
||||
}) => {
|
||||
const { workspacePage, tokensSidebar, tokenContextMenuForToken } =
|
||||
await setupTokensFile(page);
|
||||
|
||||
await page.getByRole("tab", { name: "Layers" }).click();
|
||||
|
||||
await workspacePage.layers.getByTestId("layer-row").nth(1).click();
|
||||
|
||||
// Open tokens sections on left sidebar
|
||||
const tokensTabButton = page.getByRole("tab", { name: "Tokens" });
|
||||
await tokensTabButton.click();
|
||||
|
||||
// Unfold border radius tokens
|
||||
await page.getByRole("button", { name: "Border Radius 3" }).click();
|
||||
await expect(
|
||||
tokensSidebar.getByRole("button", { name: "borderRadius" }),
|
||||
).toBeVisible();
|
||||
await tokensSidebar.getByRole("button", { name: "borderRadius" }).click();
|
||||
await expect(
|
||||
tokensSidebar.getByRole("button", { name: "borderRadius.sm" }),
|
||||
).toBeVisible();
|
||||
|
||||
// Apply border radius token from token panels
|
||||
await tokensSidebar
|
||||
.getByRole("button", { name: "borderRadius.sm" })
|
||||
.click();
|
||||
|
||||
// Check if border radius sections is visible on right sidebar
|
||||
const borderRadiusSection = page.getByRole("region", {
|
||||
name: "border-radius-section",
|
||||
});
|
||||
await expect(borderRadiusSection).toBeVisible();
|
||||
|
||||
// Check if token pill is visible on design tab on right sidebar
|
||||
const brTokenPillSM = borderRadiusSection.getByRole("button", {
|
||||
name: "borderRadius.sm",
|
||||
});
|
||||
await expect(brTokenPillSM).toBeVisible();
|
||||
await brTokenPillSM.click();
|
||||
|
||||
// Rename token
|
||||
await tokensSidebar
|
||||
.getByRole("button", { name: "borderRadius.sm" })
|
||||
.click({ button: "right" });
|
||||
await expect(page.getByText("Edit token")).toBeVisible();
|
||||
await page.getByText("Edit token").click();
|
||||
const editModal = page.getByTestId("token-update-create-modal");
|
||||
await expect(editModal).toBeVisible();
|
||||
await expect(
|
||||
editModal.getByRole("textbox", { name: "Name" }),
|
||||
).toBeVisible();
|
||||
await editModal
|
||||
.getByRole("textbox", { name: "Name" })
|
||||
.fill("BorderRadius.smBis");
|
||||
const submitButton = editModal.getByRole("button", { name: "Save" });
|
||||
await expect(submitButton).toBeEnabled();
|
||||
await submitButton.click();
|
||||
await expect(page.getByText("Don't remap")).toBeVisible();
|
||||
await page.getByText("Don't remap").click();
|
||||
const brokenPill = borderRadiusSection.getByRole("button", {
|
||||
name: "This token is not in any",
|
||||
});
|
||||
await expect(brokenPill).toBeVisible();
|
||||
|
||||
// Detach broken token
|
||||
const detachButton = borderRadiusSection.getByRole("button", {
|
||||
name: "Detach token",
|
||||
});
|
||||
await detachButton.click();
|
||||
await expect(brokenPill).not.toBeVisible();
|
||||
|
||||
//De-select and select shape again to double check token is detached
|
||||
await page.getByRole("tab", { name: "Layers" }).click();
|
||||
|
||||
await workspacePage.layers.getByTestId("layer-row").nth(0).click();
|
||||
await workspacePage.layers.getByTestId("layer-row").nth(1).click();
|
||||
await expect(brokenPill).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -706,18 +706,17 @@
|
||||
"Removes `attributes` that match `token` for `shape-ids`.
|
||||
|
||||
Doesn't update shape attributes."
|
||||
[{:keys [attributes token-name shape-ids] :as _props}]
|
||||
[{:keys [attributes token shape-ids] :as _props}]
|
||||
(ptk/reify ::unapply-token
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of
|
||||
(let [remove-token #(when % (cft/remove-attributes-for-token attributes token-name %))]
|
||||
(let [remove-token #(when % (cft/remove-attributes-for-token attributes token %))]
|
||||
(dwsh/update-shapes
|
||||
shape-ids
|
||||
(fn [shape]
|
||||
(update shape :applied-tokens remove-token))))))))
|
||||
|
||||
|
||||
(defn toggle-token
|
||||
[{:keys [token attrs shape-ids expand-with-children]}]
|
||||
(ptk/reify ::on-toggle-token
|
||||
@@ -747,7 +746,7 @@
|
||||
(if unapply-tokens?
|
||||
(rx/of
|
||||
(unapply-token {:attributes (or attrs all-attributes attributes)
|
||||
:token-name (:name token)
|
||||
:token token
|
||||
:shape-ids shape-ids}))
|
||||
(rx/of
|
||||
(cond
|
||||
|
||||
@@ -564,17 +564,18 @@
|
||||
(mf/use-fn
|
||||
(mf/deps on-detach tokens disabled token-applied)
|
||||
(fn [event]
|
||||
(when-not disabled
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(reset! token-applied* nil)
|
||||
(reset! selected-id* nil)
|
||||
(reset! focused-id* nil)
|
||||
(when on-detach
|
||||
(on-detach token-applied))
|
||||
(ts/schedule-on-idle
|
||||
(fn []
|
||||
(dom/focus! (mf/ref-val ref)))))))
|
||||
(let [token (get-token-op tokens token-applied)]
|
||||
(when-not disabled
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(reset! token-applied* nil)
|
||||
(reset! selected-id* nil)
|
||||
(reset! focused-id* nil)
|
||||
(when on-detach
|
||||
(on-detach token))
|
||||
(ts/schedule-on-idle
|
||||
(fn []
|
||||
(dom/focus! (mf/ref-val ref))))))))
|
||||
|
||||
on-token-key-down
|
||||
(mf/use-fn
|
||||
|
||||
@@ -60,8 +60,8 @@
|
||||
on-detach-token
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token-name attr]
|
||||
(st/emit! (dwta/unapply-token {:token-name token-name
|
||||
(fn [token attr]
|
||||
(st/emit! (dwta/unapply-token {:token (first token)
|
||||
:attributes #{attr}
|
||||
:shape-ids ids}))))
|
||||
|
||||
|
||||
@@ -152,9 +152,9 @@
|
||||
on-detach-token
|
||||
(mf/use-fn
|
||||
(mf/deps token-colors groups)
|
||||
(fn [token-name]
|
||||
(fn [token]
|
||||
(let [prev-colors (mf/ref-val prev-colors-ref)
|
||||
token-color (some #(when (= (:token-name %) token-name) %) token-colors)
|
||||
token-color (some #(when (= (:token-name %) (:name token)) %) token-colors)
|
||||
|
||||
[color-operations _] (retrieve-color-operations groups token-color prev-colors)]
|
||||
(doseq [op color-operations]
|
||||
@@ -166,8 +166,8 @@
|
||||
(d/without-nils))]
|
||||
(mf/set-ref-val! prev-colors-ref
|
||||
(conj prev-colors color))
|
||||
(st/emit! (dwta/unapply-token {:token-name token-name
|
||||
:attributes attr
|
||||
(st/emit! (dwta/unapply-token {:attributes attr
|
||||
:token token
|
||||
:shape-ids [(:shape-id op)]})))))))
|
||||
|
||||
select-only
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
|
||||
render-wasm? (feat/use-feature "render-wasm/v1")
|
||||
|
||||
|
||||
^boolean
|
||||
multiple? (= :multiple fills)
|
||||
|
||||
@@ -182,9 +183,9 @@
|
||||
on-detach-token
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token-name]
|
||||
(st/emit! (dwta/unapply-token {:token-name token-name
|
||||
:attributes #{:fill}
|
||||
(fn [token]
|
||||
(st/emit! (dwta/unapply-token {:attributes #{:fill}
|
||||
:token token
|
||||
:shape-ids ids}))))]
|
||||
|
||||
(mf/with-layout-effect [hide-on-export]
|
||||
@@ -214,8 +215,7 @@
|
||||
(when open?
|
||||
[:div {:class (stl/css :fill-content)}
|
||||
(cond
|
||||
(or (= :multiple fills)
|
||||
(= :multiple fill-token-applied))
|
||||
(= :multiple fills)
|
||||
[:div {:class (stl/css :fill-multiple)}
|
||||
[:div {:class (stl/css :fill-multiple-label)}
|
||||
(tr "settings.multiple")]
|
||||
|
||||
@@ -72,8 +72,8 @@
|
||||
on-detach-token
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token-name attr]
|
||||
(st/emit! (dwta/unapply-token {:token-name token-name
|
||||
(fn [token attr]
|
||||
(st/emit! (dwta/unapply-token {:token (first token)
|
||||
:attributes #{attr}
|
||||
:shape-ids ids}))))
|
||||
|
||||
@@ -229,13 +229,13 @@
|
||||
:property (tr "workspace.options.opacity")
|
||||
:applied-token (get applied-tokens :opacity)
|
||||
:placeholder (if (or (= :multiple (get applied-tokens :opacity))
|
||||
(= :multiple (or (get values :opacity) 1)))
|
||||
(= :multiple (or (get values name) 1)))
|
||||
(tr "settings.multiple")
|
||||
"--")
|
||||
:align :right
|
||||
:class (stl/css :numeric-input-wrapper)
|
||||
:value (* 100
|
||||
(or (get values :opacity) 1))}]
|
||||
(or (get values name) 1))}]
|
||||
|
||||
[:div {:class (stl/css :input)
|
||||
:title (tr "workspace.options.opacity")}
|
||||
@@ -248,6 +248,7 @@
|
||||
:max 100
|
||||
:className (stl/css :numeric-input)}]])
|
||||
|
||||
|
||||
[:div {:class (stl/css :actions)}
|
||||
(cond
|
||||
(or (= :multiple hidden?) (not hidden?))
|
||||
|
||||
@@ -339,8 +339,8 @@
|
||||
on-detach-token
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token-name attr]
|
||||
(st/emit! (dwta/unapply-token {:token-name token-name
|
||||
(fn [token attr]
|
||||
(st/emit! (dwta/unapply-token {:token (first token)
|
||||
:attributes #{attr}
|
||||
:shape-ids ids}))))
|
||||
|
||||
@@ -475,7 +475,7 @@
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token attr]
|
||||
(st/emit! (dwta/unapply-token {:token-name token
|
||||
(st/emit! (dwta/unapply-token {:token (first token)
|
||||
:attributes #{attr}
|
||||
:shape-ids ids}))))
|
||||
|
||||
@@ -722,7 +722,7 @@
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token attr]
|
||||
(st/emit! (dwta/unapply-token {:token-name token
|
||||
(st/emit! (dwta/unapply-token {:token (first token)
|
||||
:attributes #{attr}
|
||||
:shape-ids ids}))))
|
||||
|
||||
|
||||
@@ -97,8 +97,8 @@
|
||||
on-detach-token
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token-name attr]
|
||||
(st/emit! (dwta/unapply-token {:token-name token-name
|
||||
(fn [token attr]
|
||||
(st/emit! (dwta/unapply-token {:token (first token)
|
||||
:attributes #{attr}
|
||||
:shape-ids ids}))))
|
||||
|
||||
@@ -220,8 +220,8 @@
|
||||
on-detach-token
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token-name attr]
|
||||
(st/emit! (dwta/unapply-token {:token-name token-name
|
||||
(fn [token attr]
|
||||
(st/emit! (dwta/unapply-token {:token (first token)
|
||||
:attributes #{attr}
|
||||
:shape-ids ids}))))
|
||||
|
||||
@@ -550,8 +550,8 @@
|
||||
on-detach-token
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token-name attr]
|
||||
(st/emit! (dwta/unapply-token {:token-name token-name
|
||||
(fn [token attr]
|
||||
(st/emit! (dwta/unapply-token {:token (first token)
|
||||
:attributes #{attr}
|
||||
:shape-ids ids}))))
|
||||
|
||||
|
||||
@@ -319,8 +319,8 @@
|
||||
on-detach-token
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token-name attr]
|
||||
(st/emit! (dwta/unapply-token {:token-name token-name
|
||||
(fn [token attr]
|
||||
(st/emit! (dwta/unapply-token {:token (first token)
|
||||
:attributes #{attr}
|
||||
:shape-ids ids}))))
|
||||
|
||||
|
||||
@@ -171,9 +171,9 @@
|
||||
on-detach-token
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token-name attrs]
|
||||
(st/emit! (dwta/unapply-token {:token-name token-name
|
||||
:attributes attrs
|
||||
(fn [token attrs]
|
||||
(st/emit! (dwta/unapply-token {:attributes attrs
|
||||
:token token
|
||||
:shape-ids ids}))))]
|
||||
|
||||
[:section {:class (stl/css :stroke-section)
|
||||
|
||||
@@ -85,14 +85,14 @@
|
||||
(mf/use-fn
|
||||
(mf/deps detach-token token applied-token-name)
|
||||
(fn []
|
||||
(let [token-name (or (:name token) applied-token-name)]
|
||||
(detach-token token-name))))
|
||||
(let [token (or token applied-token-name)]
|
||||
(detach-token token))))
|
||||
|
||||
has-errors (some? (:errors token))
|
||||
token-name (:name token)
|
||||
resolved (:resolved-value token)
|
||||
not-active (or (empty? active-tokens)
|
||||
(nil? token))
|
||||
not-active (and (empty? active-tokens)
|
||||
(nil? token))
|
||||
id (dm/str (:id token) "-name")
|
||||
swatch-tooltip-content (cond
|
||||
not-active
|
||||
@@ -344,6 +344,7 @@
|
||||
(mf/with-effect [color prev-color disable-picker]
|
||||
(when (and (not disable-picker) (not= prev-color color))
|
||||
(modal/update-props! :colorpicker {:data (parse-color color)})))
|
||||
|
||||
[:div {:class [class row-class]}
|
||||
;; Drag handler
|
||||
(when (some? on-reorder)
|
||||
|
||||
@@ -64,17 +64,14 @@
|
||||
(let [selected? (selected-pred attribute)
|
||||
props {:attributes #{attribute}
|
||||
:token token
|
||||
:shape-ids shape-ids}
|
||||
unnaply-props {:token-name (:name token)
|
||||
:attributes #{attribute}
|
||||
:shape-ids shape-ids}]
|
||||
:shape-ids shape-ids}]
|
||||
|
||||
{:title title
|
||||
:hint hint
|
||||
:selected? selected?
|
||||
:action (fn []
|
||||
(if selected?
|
||||
(st/emit! (dwta/unapply-token unnaply-props))
|
||||
(st/emit! (dwta/unapply-token props))
|
||||
(st/emit! (dwta/apply-token (assoc props :on-update-shape on-update-shape-fn)))))}))
|
||||
allowed-attributes)))
|
||||
|
||||
@@ -85,15 +82,12 @@
|
||||
{:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes attributes)
|
||||
all-action (let [props {:attributes attributes
|
||||
:token token
|
||||
:shape-ids shape-ids}
|
||||
unnaply-props {:token-name (:name token)
|
||||
:attributes attributes
|
||||
:shape-ids shape-ids}]
|
||||
:shape-ids shape-ids}]
|
||||
{:title (tr "labels.all")
|
||||
:selected? all-selected?
|
||||
:hint hint
|
||||
:action #(if all-selected?
|
||||
(st/emit! (dwta/unapply-token unnaply-props))
|
||||
(st/emit! (dwta/unapply-token props))
|
||||
(st/emit! (dwta/apply-token (assoc props :on-update-shape (or on-update-shape-all on-update-shape)))))})
|
||||
single-actions (map (fn [[attr title]]
|
||||
(let [selected? (selected-pred attr)]
|
||||
@@ -102,13 +96,10 @@
|
||||
:action #(let [props {:attributes #{attr}
|
||||
:token token
|
||||
:shape-ids shape-ids}
|
||||
unnaply-props {:token-name (:name token)
|
||||
:attributes #{attr}
|
||||
:shape-ids shape-ids}
|
||||
event (cond
|
||||
all-selected? (-> (assoc props :attributes-to-remove attributes)
|
||||
(dwta/apply-token))
|
||||
selected? (dwta/unapply-token unnaply-props)
|
||||
selected? (dwta/unapply-token props)
|
||||
:else (-> (assoc props :on-update-shape on-update-shape)
|
||||
(dwta/apply-token)))]
|
||||
(st/emit! event))}))
|
||||
@@ -132,12 +123,9 @@
|
||||
:action (fn []
|
||||
(let [props {:attributes attrs
|
||||
:token token
|
||||
:shape-ids shape-ids}
|
||||
unnaply-props {:token-name (:name token)
|
||||
:attributes attrs
|
||||
:shape-ids shape-ids}]
|
||||
:shape-ids shape-ids}]
|
||||
(if all-selected?
|
||||
(st/emit! (dwta/unapply-token unnaply-props))
|
||||
(st/emit! (dwta/unapply-token props))
|
||||
(st/emit! (dwta/apply-token (assoc props :on-update-shape on-update-shape))))))}
|
||||
{:title "Horizontal"
|
||||
:selected? horizontal-selected?
|
||||
@@ -177,13 +165,10 @@
|
||||
:action #(let [props {:attributes #{attr}
|
||||
:token token
|
||||
:shape-ids shape-ids}
|
||||
unnaply-props {:token-name (:name token)
|
||||
:attributes #{attr}
|
||||
:shape-ids shape-ids}
|
||||
event (cond
|
||||
all-selected? (-> (assoc props :attributes-to-remove attrs)
|
||||
(dwta/apply-token))
|
||||
selected? (dwta/unapply-token unnaply-props)
|
||||
selected? (dwta/unapply-token props)
|
||||
:else (-> (assoc props :on-update-shape on-update-shape)
|
||||
(dwta/apply-token)))]
|
||||
(st/emit! event))}))
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
[app.main.ui.ds.buttons.button :refer [button*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.ds.foundations.typography.heading :refer [heading*]]
|
||||
[app.main.ui.ds.notifications.context-notification :refer [context-notification*]]
|
||||
[app.main.ui.forms :as fc]
|
||||
[app.main.ui.workspace.tokens.management.forms.controls :as token.controls]
|
||||
[app.main.ui.workspace.tokens.management.forms.validators :refer [default-validate-token]]
|
||||
@@ -142,6 +143,10 @@
|
||||
(fm/use-form :schema schema
|
||||
:initial initial)
|
||||
|
||||
warning-name-change?
|
||||
(not= (get-in @form [:data :name])
|
||||
(:name initial))
|
||||
|
||||
on-toggle-tab
|
||||
(mf/use-fn
|
||||
(mf/deps form)
|
||||
@@ -271,7 +276,12 @@
|
||||
:max-length max-input-length
|
||||
:variant "comfortable"
|
||||
:trim true
|
||||
:auto-focus true}]]
|
||||
:auto-focus true}]
|
||||
|
||||
(when (and warning-name-change? (= action "edit"))
|
||||
[:div {:class (stl/css :warning-name-change-notification-wrapper)}
|
||||
[:> context-notification*
|
||||
{:level :warning :appearance :ghost} (tr "workspace.tokens.warning-name-change")]])]
|
||||
|
||||
[:div {:class (stl/css :input-row)}
|
||||
(case type
|
||||
|
||||
@@ -177,7 +177,7 @@
|
||||
;; ==== Action
|
||||
events [(dwta/unapply-token {:shape-ids [(cthi/id :frame1)]
|
||||
:attributes #{:r1 :r2 :r3 :r4}
|
||||
:token-name "test-token-1"})]
|
||||
:token (toht/get-token file "test-token-1")})]
|
||||
|
||||
step2 (fn [_]
|
||||
(let [events2 [(dwl/sync-file (:id file) (:id file))]]
|
||||
@@ -289,7 +289,7 @@
|
||||
;; ==== Action
|
||||
events [(dwta/unapply-token {:shape-ids [(cthi/id :c-frame1)]
|
||||
:attributes #{:r1 :r2 :r3 :r4}
|
||||
:token-name "test-token-1"})
|
||||
:token (toht/get-token file "test-token-1")})
|
||||
(dwta/apply-token {:shape-ids [(cthi/id :frame1)]
|
||||
:attributes #{:r1 :r2 :r3 :r4}
|
||||
:token (toht/get-token file "test-token-3")
|
||||
|
||||
@@ -19,7 +19,7 @@ In the `apps` folder you'll find some examples that use the libraries mentioned
|
||||
- example-styles: to run this example you should run
|
||||
|
||||
```
|
||||
npm run start:styles-example
|
||||
pnpm run start:styles-example
|
||||
```
|
||||
|
||||
Open in your browser: `http://localhost:4202/`
|
||||
@@ -28,8 +28,8 @@ Open in your browser: `http://localhost:4202/`
|
||||
|
||||
This guide will help you launch a Penpot plugin from the penpot-plugins repository. Before proceeding, ensure that you have Penpot running locally by following the [setup instructions](https://help.penpot.app/technical-guide/developer/devenv/).
|
||||
|
||||
In the terminal, navigate to the **penpot-plugins** repository and run `npm install` to install the required dependencies.
|
||||
Then, run `npm start` to launch the plugins wrapper.
|
||||
In the terminal, navigate to the **penpot-plugins** repository and run `pnpm install` to install the required dependencies.
|
||||
Then, run `pnpm run start` to launch the plugins wrapper.
|
||||
|
||||
After installing the dependencies, choose a plugin to launch. You can either run one of the provided examples or create your own (see "Creating a plugin from scratch" below).
|
||||
To launch a plugin, Open a new terminal tab and run the appropriate startup script for the chosen plugin.
|
||||
@@ -38,7 +38,7 @@ For instance, to launch the Contrast plugin, use the following command:
|
||||
|
||||
```
|
||||
// for the contrast plugin
|
||||
npm run start:plugin:contrast
|
||||
pnpm run start:plugin:contrast
|
||||
```
|
||||
|
||||
Finally, open in your browser the specific port. In this specific example would be `http://localhost:4302`
|
||||
@@ -49,21 +49,22 @@ A table listing the available plugins and their corresponding startup commands i
|
||||
|
||||
| Plugin | Description | PORT | Start command | Manifest URL |
|
||||
| ----------------------- | ----------------------------------------------------------- | ---- | ------------------------------------- | ------------------------------------------ |
|
||||
| poc-state-plugin | Sandbox plugin to test new plugins api functionality | 4301 | npm run start:plugin:poc-state | http://localhost:4301/assets/manifest.json |
|
||||
| contrast-plugin | Sample plugin that gives you color contrast information | 4302 | npm run start:plugin:contrast | http://localhost:4302/assets/manifest.json |
|
||||
| icons-plugin | Tool to add icons from [Feather](https://feathericons.com/) | 4303 | npm run start:plugin:icons | http://localhost:4303/assets/manifest.json |
|
||||
| lorem-ipsum-plugin | Generate Lorem ipsum text | 4304 | npm run start:plugin:loremipsum | http://localhost:4304/assets/manifest.json |
|
||||
| create-palette-plugin | Creates a board with all the palette colors | 4305 | npm run start:plugin:palette | http://localhost:4305/assets/manifest.json |
|
||||
| table-plugin | Create or import table | 4306 | npm run start:table-plugin | http://localhost:4306/assets/manifest.json |
|
||||
| rename-layers-plugin | Rename layers in bulk | 4307 | npm run start:plugin:renamelayers | http://localhost:4307/assets/manifest.json |
|
||||
| colors-to-tokens-plugin | Generate tokens JSON file | 4308 | npm run start:plugin:colors-to-tokens | http://localhost:4308/assets/manifest.json |
|
||||
| poc-state-plugin | Sandbox plugin to test new plugins api functionality | 4301 | pnpm run start:plugin:poc-state | http://localhost:4301/assets/manifest.json |
|
||||
| contrast-plugin | Sample plugin that gives you color contrast information | 4302 | pnpm run start:plugin:contrast | http://localhost:4302/assets/manifest.json |
|
||||
| icons-plugin | Tool to add icons from [Feather](https://feathericons.com/) | 4303 | pnpm run start:plugin:icons | http://localhost:4303/assets/manifest.json |
|
||||
| lorem-ipsum-plugin | Generate Lorem ipsum text | 4304 | pnpm run start:plugin:loremipsum | http://localhost:4304/assets/manifest.json |
|
||||
| create-palette-plugin | Creates a board with all the palette colors | 4305 | pnpm run start:plugin:palette | http://localhost:4305/assets/manifest.json |
|
||||
| table-plugin | Create or import table | 4306 | pnpm run start:table-plugin | http://localhost:4306/assets/manifest.json |
|
||||
| rename-layers-plugin | Rename layers in bulk | 4307 | pnpm run start:plugin:renamelayers | http://localhost:4307/assets/manifest.json |
|
||||
| colors-to-tokens-plugin | Generate tokens JSON file | 4308 | pnpm run start:plugin:colors-to-tokens | http://localhost:4308/assets/manifest.json |
|
||||
| poc-tokens-plugin | Sandbox plugin to test tokens functionality | 4309 | pnpm run start:plugin:poc-tokens | http://localhost:4309/assets/manifest.json |
|
||||
|
||||
## Web Apps
|
||||
|
||||
| App | Description | PORT | Start command | URL |
|
||||
| --------------- | ----------------------------------------------------------------- | ---- | -------------------------------- | ---------------------- |
|
||||
| plugins-runtime | Runtime for the plugins subsystem | 4200 | npm run start:app:runtime | |
|
||||
| example-styles | Showcase of some of the Penpot styles that can be used in plugins | 4201 | npm run start:app:styles-example | http://localhost:4201/ |
|
||||
| plugins-runtime | Runtime for the plugins subsystem | 4200 | pnpm run start:app:runtime | |
|
||||
| example-styles | Showcase of some of the Penpot styles that can be used in plugins | 4201 | pnpm run start:app:styles-example | http://localhost:4201/ |
|
||||
|
||||
## Creating a plugin from scratch
|
||||
|
||||
|
||||
51
plugins/apps/poc-tokens-plugin/eslint.config.js
Normal file
51
plugins/apps/poc-tokens-plugin/eslint.config.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import baseConfig from '../../eslint.config.js';
|
||||
import { compat } from '../../eslint.base.config.js';
|
||||
|
||||
export default [
|
||||
...baseConfig,
|
||||
...compat
|
||||
.config({
|
||||
extends: [
|
||||
'plugin:@nx/angular',
|
||||
'plugin:@angular-eslint/template/process-inline-templates',
|
||||
],
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'app',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'app',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
})),
|
||||
...compat
|
||||
.config({ extends: ['plugin:@nx/angular-template'] })
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.html'],
|
||||
rules: {},
|
||||
})),
|
||||
{ ignores: ['**/assets/*.js'] },
|
||||
{
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project: './tsconfig.*?.json',
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
79
plugins/apps/poc-tokens-plugin/project.json
Normal file
79
plugins/apps/poc-tokens-plugin/project.json
Normal file
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"name": "poc-tokens-plugin",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "application",
|
||||
"prefix": "app",
|
||||
"sourceRoot": "apps/poc-tokens-plugin/src",
|
||||
"tags": ["type:plugin"],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@angular-devkit/build-angular:application",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"outputPath": "dist/apps/poc-tokens-plugin",
|
||||
"index": "apps/poc-tokens-plugin/src/index.html",
|
||||
"browser": "apps/poc-tokens-plugin/src/main.ts",
|
||||
"polyfills": ["zone.js"],
|
||||
"tsConfig": "apps/poc-tokens-plugin/tsconfig.app.json",
|
||||
"assets": [
|
||||
"apps/poc-tokens-plugin/src/favicon.ico",
|
||||
"apps/poc-tokens-plugin/src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"libs/plugins-styles/src/lib/styles.css",
|
||||
"apps/poc-tokens-plugin/src/styles.css"
|
||||
],
|
||||
"scripts": [],
|
||||
"optimization": {
|
||||
"scripts": true,
|
||||
"styles": true,
|
||||
"fonts": false
|
||||
}
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kb",
|
||||
"maximumError": "1mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"optimization": false,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production",
|
||||
"dependsOn": ["buildPlugin"]
|
||||
},
|
||||
"serve": {
|
||||
"executor": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"buildTarget": "poc-tokens-plugin:build:production"
|
||||
},
|
||||
"development": {
|
||||
"buildTarget": "poc-tokens-plugin:build:development",
|
||||
"port": 4309,
|
||||
"host": "0.0.0.0"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"executor": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"buildTarget": "poc-tokens-plugin:build"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
127
plugins/apps/poc-tokens-plugin/src/app/app.component.css
Normal file
127
plugins/apps/poc-tokens-plugin/src/app/app.component.css
Normal file
@@ -0,0 +1,127 @@
|
||||
/* @import "@penpot/plugin-styles/styles.css"; */
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.title-l {
|
||||
margin: var(--spacing-16) 0;
|
||||
}
|
||||
|
||||
.columns {
|
||||
display: grid;
|
||||
grid-template-columns: 50% 50%;
|
||||
flex-grow: 1;
|
||||
margin-block-end: var(--spacing-16);
|
||||
}
|
||||
|
||||
.panels {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
padding: 0 var(--spacing-8);
|
||||
}
|
||||
|
||||
.panel {
|
||||
padding: var(--spacing-8);
|
||||
display: flex;
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.panel:not(:first-child) {
|
||||
border-block-start: 1px solid var(--df-secondary);
|
||||
padding-block-start: var(--spacing-16);
|
||||
}
|
||||
|
||||
.panel-heading,
|
||||
.token-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding-inline-end: var(--spacing-8);
|
||||
}
|
||||
|
||||
.panel-heading p,
|
||||
.token-group span {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.panel-heading button,
|
||||
.token-group button {
|
||||
background: none;
|
||||
padding: var(--spacing-4) calc(var(--spacing-12) / 2);
|
||||
}
|
||||
|
||||
.panel-heading button:focus,
|
||||
.token-group button:focus {
|
||||
padding: calc(var(--spacing-4) - 2px) calc(var(--spacing-12) / 2 - 2px);
|
||||
}
|
||||
|
||||
.panel-item button {
|
||||
opacity: 0;
|
||||
margin-inline-end: var(--spacing-8);
|
||||
padding: var(--spacing-4) calc(var(--spacing-12) / 2);
|
||||
}
|
||||
|
||||
.panel-item button:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.panel-item button:focus {
|
||||
opacity: 1;
|
||||
padding: calc(var(--spacing-4) - 2px) calc(var(--spacing-12) / 2 - 2px);
|
||||
}
|
||||
|
||||
.panel ul {
|
||||
/* flex-grow: 1; */
|
||||
overflow-y: auto;
|
||||
padding-inline-end: var(--spacing-8);
|
||||
}
|
||||
|
||||
.panel-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.panel-item span {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.set-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.set-item.selected {
|
||||
background-color: var(--db-quaternary);
|
||||
}
|
||||
|
||||
.set-item:hover {
|
||||
color: var(--da-primary);
|
||||
background-color: var(--db-secondary);
|
||||
}
|
||||
|
||||
.token-group:not(:first-child) {
|
||||
margin-top: var(--spacing-8);
|
||||
}
|
||||
|
||||
.token-group {
|
||||
border-block-end: 1px solid var(--df-secondary);
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.token-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.token-item:hover {
|
||||
color: var(--da-primary);
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
144
plugins/apps/poc-tokens-plugin/src/app/app.component.html
Normal file
144
plugins/apps/poc-tokens-plugin/src/app/app.component.html
Normal file
@@ -0,0 +1,144 @@
|
||||
<div class="container">
|
||||
<p class="title-l">Design tokens plugin POC</p>
|
||||
|
||||
<div class="columns">
|
||||
<div class="panels">
|
||||
<div class="panel">
|
||||
<div class="panel-heading">
|
||||
<p class="headline-m">THEMES</p>
|
||||
<button
|
||||
type="button"
|
||||
data-appearance="secondary"
|
||||
(click)="addTheme()"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ul data-handler="themes-list">
|
||||
@for (theme of themes; track theme.id) {
|
||||
<li class="body-m panel-item theme-item">
|
||||
<span>{{ theme.group }} / {{ theme.name }}</span>
|
||||
<button
|
||||
type="button"
|
||||
data-appearance="secondary"
|
||||
(click)="renameTheme(theme.id, theme.name)"
|
||||
>
|
||||
🖊️
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-appearance="secondary"
|
||||
(click)="deleteTheme(theme.id)"
|
||||
>
|
||||
❌
|
||||
</button>
|
||||
<div class="checkbox-container">
|
||||
<input
|
||||
class="checkbox-input"
|
||||
type="checkbox"
|
||||
id="checkbox1"
|
||||
[checked]="isThemeActive(theme.id)"
|
||||
(change)="toggleTheme(theme.id)"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-heading">
|
||||
<p class="headline-m">SETS</p>
|
||||
<button type="button" data-appearance="secondary" (click)="addSet()">
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ul data-handler="sets-list">
|
||||
@for (set of sets; track set.id) {
|
||||
<li
|
||||
class="body-m panel-item set-item"
|
||||
[class.selected]="set.id === currentSetId"
|
||||
>
|
||||
<span (click)="loadTokens(set.id)">
|
||||
{{ set.name }}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
data-appearance="secondary"
|
||||
(click)="renameSet(set.id, set.name)"
|
||||
>
|
||||
🖊️
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-appearance="secondary"
|
||||
(click)="deleteSet(set.id)"
|
||||
>
|
||||
❌
|
||||
</button>
|
||||
<div class="checkbox-container">
|
||||
<input
|
||||
class="checkbox-input"
|
||||
type="checkbox"
|
||||
id="checkbox1"
|
||||
[checked]="isSetActive(set.id)"
|
||||
(change)="toggleSet(set.id)"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panels">
|
||||
<div class="panel">
|
||||
<p class="headline-m">TOKENS</p>
|
||||
|
||||
<ul data-handler="tokens-list">
|
||||
@for (group of tokenGroups; track group[0]) {
|
||||
<li class="body-m token-group">
|
||||
<span>{{ group[0] }}</span>
|
||||
<button
|
||||
type="button"
|
||||
data-appearance="secondary"
|
||||
(click)="addToken(group[0])"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</li>
|
||||
@for (token of group[1]; track token.id) {
|
||||
<li
|
||||
class="body-m panel-item token-item"
|
||||
(click)="applyToken(token.id)"
|
||||
>
|
||||
<span>{{ token.name }}</span>
|
||||
<button
|
||||
type="button"
|
||||
data-appearance="secondary"
|
||||
(click)="renameToken(token.id, token.name)"
|
||||
>
|
||||
🖊️
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-appearance="secondary"
|
||||
(click)="deleteToken(token.id)"
|
||||
>
|
||||
❌
|
||||
</button>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="buttons">
|
||||
<button type="button" data-appearance="primary" (click)="loadLibrary()">
|
||||
Load
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
290
plugins/apps/poc-tokens-plugin/src/app/app.component.ts
Normal file
290
plugins/apps/poc-tokens-plugin/src/app/app.component.ts
Normal file
@@ -0,0 +1,290 @@
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { fromEvent, map, filter, take, merge } from 'rxjs';
|
||||
import { PluginMessageEvent, PluginUIEvent } from '../model';
|
||||
|
||||
type TokenTheme = {
|
||||
id: string;
|
||||
name: string;
|
||||
group: string;
|
||||
description: string;
|
||||
active: boolean;
|
||||
};
|
||||
|
||||
type TokenSet = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
active: boolean;
|
||||
};
|
||||
|
||||
type Token = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
type TokensGroup = [string, Token[]];
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrl: './app.component.css',
|
||||
host: {
|
||||
'[attr.data-theme]': 'theme()',
|
||||
},
|
||||
})
|
||||
export class AppComponent {
|
||||
public route = inject(ActivatedRoute);
|
||||
|
||||
public messages$ = fromEvent<MessageEvent<PluginMessageEvent>>(
|
||||
window,
|
||||
'message',
|
||||
);
|
||||
|
||||
public initialTheme$ = this.route.queryParamMap.pipe(
|
||||
map((params) => params.get('theme')),
|
||||
filter((theme) => !!theme),
|
||||
take(1),
|
||||
);
|
||||
|
||||
public theme = toSignal(
|
||||
merge(
|
||||
this.initialTheme$,
|
||||
this.messages$.pipe(
|
||||
filter((event) => event.data.type === 'theme'),
|
||||
map((event) => {
|
||||
return event.data.content;
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
public themes: TokenTheme[] = [];
|
||||
public sets: TokenSet[] = [];
|
||||
public tokenGroups: TokensGroup[] = [];
|
||||
public currentSetId: string | undefined = undefined;
|
||||
|
||||
constructor() {
|
||||
window.addEventListener('message', (event) => {
|
||||
if (event.data.type === 'set-themes') {
|
||||
this.#setThemes(event.data.themesData);
|
||||
} else if (event.data.type === 'set-sets') {
|
||||
this.#setSets(event.data.setsData);
|
||||
} else if (event.data.type === 'set-tokens') {
|
||||
this.#setTokens(event.data.tokenGroupsData);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
loadLibrary() {
|
||||
this.#sendMessage({ type: 'load-library' });
|
||||
}
|
||||
|
||||
loadTokens(setId: string) {
|
||||
this.currentSetId = setId;
|
||||
this.#sendMessage({ type: 'load-tokens', setId });
|
||||
}
|
||||
|
||||
addTheme() {
|
||||
this.#sendMessage({
|
||||
type: 'add-theme',
|
||||
themeGroup: this.#randomString(),
|
||||
themeName: this.#randomString(),
|
||||
});
|
||||
}
|
||||
|
||||
addSet() {
|
||||
this.#sendMessage({ type: 'add-set', setName: this.#randomString() });
|
||||
}
|
||||
|
||||
addToken(tokenType: string) {
|
||||
let tokenValue;
|
||||
switch (tokenType) {
|
||||
case 'borderRadius':
|
||||
tokenValue = 25;
|
||||
break;
|
||||
case 'shadow':
|
||||
tokenValue = [
|
||||
{
|
||||
color: '#123456',
|
||||
inset: 'false',
|
||||
offsetX: '6',
|
||||
offsetY: '6',
|
||||
spread: '0',
|
||||
blur: '4',
|
||||
},
|
||||
];
|
||||
break;
|
||||
case 'color':
|
||||
tokenValue = '#fabada';
|
||||
break;
|
||||
case 'dimension':
|
||||
tokenValue = 100;
|
||||
break;
|
||||
case 'fontFamilies':
|
||||
tokenValue = ['Source Sans Pro', 'Sans serif'];
|
||||
break;
|
||||
case 'fontSizes':
|
||||
tokenValue = 24;
|
||||
break;
|
||||
case 'fontWeights':
|
||||
tokenValue = 'bold';
|
||||
break;
|
||||
case 'letterSpacing':
|
||||
tokenValue = 0.5;
|
||||
break;
|
||||
case 'number':
|
||||
tokenValue = 33;
|
||||
break;
|
||||
case 'opacity':
|
||||
tokenValue = 0.6;
|
||||
break;
|
||||
case 'rotation':
|
||||
tokenValue = 45;
|
||||
break;
|
||||
case 'sizing':
|
||||
tokenValue = 200;
|
||||
break;
|
||||
case 'spacing':
|
||||
tokenValue = 16;
|
||||
break;
|
||||
case 'borderWidth':
|
||||
tokenValue = 3;
|
||||
break;
|
||||
case 'textCase':
|
||||
tokenValue = 'lowercase';
|
||||
break;
|
||||
case 'textDecoration':
|
||||
tokenValue = 'underline';
|
||||
break;
|
||||
case 'typography':
|
||||
tokenValue = {
|
||||
fontFamilies: ['Acme', 'Arial', 'Sans Serif'],
|
||||
fontSizes: '36',
|
||||
letterSpacing: '0.8',
|
||||
textCase: 'uppercase',
|
||||
textDecoration: 'none',
|
||||
fontWeights: '600',
|
||||
lineHeight: '1.5',
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.currentSetId && tokenValue) {
|
||||
this.#sendMessage({
|
||||
type: 'add-token',
|
||||
setId: this.currentSetId,
|
||||
tokenType,
|
||||
tokenName: this.#randomString(),
|
||||
tokenValue,
|
||||
});
|
||||
} else {
|
||||
console.log('Invalid token type');
|
||||
}
|
||||
}
|
||||
|
||||
renameTheme(themeId: string, themeName: string) {
|
||||
const newName = prompt('Rename theme', themeName);
|
||||
if (newName && newName !== '') {
|
||||
this.#sendMessage({ type: 'rename-theme', themeId, newName });
|
||||
}
|
||||
}
|
||||
|
||||
renameSet(setId: string, setName: string) {
|
||||
const newName = prompt('Rename set', setName);
|
||||
if (newName && newName !== '') {
|
||||
this.#sendMessage({ type: 'rename-set', setId, newName });
|
||||
}
|
||||
}
|
||||
|
||||
renameToken(tokenId: string, tokenName: string) {
|
||||
const newName = prompt('Rename token', tokenName);
|
||||
if (this.currentSetId && newName && newName !== '') {
|
||||
this.#sendMessage({
|
||||
type: 'rename-token',
|
||||
setId: this.currentSetId,
|
||||
tokenId,
|
||||
newName,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
deleteTheme(themeId: string) {
|
||||
this.#sendMessage({ type: 'delete-theme', themeId });
|
||||
}
|
||||
|
||||
deleteSet(setId: string) {
|
||||
this.#sendMessage({ type: 'delete-set', setId });
|
||||
}
|
||||
|
||||
deleteToken(tokenId: string) {
|
||||
if (this.currentSetId) {
|
||||
this.#sendMessage({
|
||||
type: 'delete-token',
|
||||
setId: this.currentSetId,
|
||||
tokenId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
isThemeActive(themeId: string) {
|
||||
for (const theme of this.themes) {
|
||||
if (theme.id === themeId) {
|
||||
return theme.active;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
toggleTheme(themeId: string) {
|
||||
this.#sendMessage({ type: 'toggle-theme', themeId });
|
||||
}
|
||||
|
||||
isSetActive(setId: string) {
|
||||
for (const set of this.sets) {
|
||||
if (set.id === setId) {
|
||||
return set.active;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
toggleSet(setId: string) {
|
||||
this.#sendMessage({ type: 'toggle-set', setId });
|
||||
}
|
||||
|
||||
applyToken(tokenId: string) {
|
||||
if (this.currentSetId) {
|
||||
this.#sendMessage({
|
||||
type: 'apply-token',
|
||||
setId: this.currentSetId,
|
||||
tokenId,
|
||||
// attributes: ['stroke-color'] // Uncomment to choose attribute to apply
|
||||
}); // (incompatible attributes will have no effect)
|
||||
}
|
||||
}
|
||||
|
||||
#sendMessage(message: PluginUIEvent) {
|
||||
parent.postMessage(message, '*');
|
||||
}
|
||||
|
||||
#setThemes(themes: TokenTheme[]) {
|
||||
this.themes = themes;
|
||||
}
|
||||
|
||||
#setSets(sets: TokenSet[]) {
|
||||
this.sets = sets;
|
||||
}
|
||||
|
||||
#setTokens(tokenGroups: TokensGroup[]) {
|
||||
this.tokenGroups = tokenGroups;
|
||||
}
|
||||
|
||||
#randomString() {
|
||||
// Generate a big random number and convert it to string using base 36
|
||||
// (the number of letters in the ascii alphabet)
|
||||
return Math.floor(Math.random() * Date.now()).toString(36);
|
||||
}
|
||||
}
|
||||
11
plugins/apps/poc-tokens-plugin/src/app/app.config.ts
Normal file
11
plugins/apps/poc-tokens-plugin/src/app/app.config.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
|
||||
import { routes } from './app.routes';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideZoneChangeDetection({ eventCoalescing: true }),
|
||||
provideRouter(routes),
|
||||
],
|
||||
};
|
||||
3
plugins/apps/poc-tokens-plugin/src/app/app.routes.ts
Normal file
3
plugins/apps/poc-tokens-plugin/src/app/app.routes.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
export const routes: Routes = [];
|
||||
1
plugins/apps/poc-tokens-plugin/src/assets/CORS
Normal file
1
plugins/apps/poc-tokens-plugin/src/assets/CORS
Normal file
@@ -0,0 +1 @@
|
||||
*
|
||||
2
plugins/apps/poc-tokens-plugin/src/assets/_headers
Normal file
2
plugins/apps/poc-tokens-plugin/src/assets/_headers
Normal file
@@ -0,0 +1,2 @@
|
||||
/*
|
||||
Access-Control-Allow-Origin: *
|
||||
BIN
plugins/apps/poc-tokens-plugin/src/assets/favicon.ico
Normal file
BIN
plugins/apps/poc-tokens-plugin/src/assets/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
plugins/apps/poc-tokens-plugin/src/assets/icon.png
Normal file
BIN
plugins/apps/poc-tokens-plugin/src/assets/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
14
plugins/apps/poc-tokens-plugin/src/assets/manifest.json
Normal file
14
plugins/apps/poc-tokens-plugin/src/assets/manifest.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "Design tokens plugin POC",
|
||||
"description": "This is a plugin to try Design Tokens in Penpot API",
|
||||
"code": "/assets/plugin.js",
|
||||
"permissions": [
|
||||
"page:read",
|
||||
"content:read",
|
||||
"file:read",
|
||||
"selection:read",
|
||||
"content:write",
|
||||
"library:read",
|
||||
"library:write"
|
||||
]
|
||||
}
|
||||
13
plugins/apps/poc-tokens-plugin/src/index.html
Normal file
13
plugins/apps/poc-tokens-plugin/src/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Angular example 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>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
7
plugins/apps/poc-tokens-plugin/src/main.ts
Normal file
7
plugins/apps/poc-tokens-plugin/src/main.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { appConfig } from './app/app.config';
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||
console.error(err),
|
||||
);
|
||||
112
plugins/apps/poc-tokens-plugin/src/model.ts
Normal file
112
plugins/apps/poc-tokens-plugin/src/model.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { TokenProperty } from '@penpot/plugin-types';
|
||||
|
||||
/**
|
||||
* This file contains the typescript interfaces for the plugin events.
|
||||
*/
|
||||
|
||||
// Events sent from the ui to the plugin
|
||||
|
||||
export interface LoadLibraryEvent {
|
||||
type: 'load-library';
|
||||
}
|
||||
|
||||
export interface LoadTokensEvent {
|
||||
type: 'load-tokens';
|
||||
setId: string;
|
||||
}
|
||||
|
||||
export interface AddThemeEvent {
|
||||
type: 'add-theme';
|
||||
themeGroup: string;
|
||||
themeName: string;
|
||||
}
|
||||
|
||||
export interface AddSetEvent {
|
||||
type: 'add-set';
|
||||
setName: string;
|
||||
}
|
||||
|
||||
export interface AddTokenEvent {
|
||||
type: 'add-token';
|
||||
setId: string;
|
||||
tokenType: string;
|
||||
tokenName: string;
|
||||
tokenValue: unknown;
|
||||
}
|
||||
|
||||
export interface RenameThemeEvent {
|
||||
type: 'rename-theme';
|
||||
themeId: string;
|
||||
newName: string;
|
||||
}
|
||||
|
||||
export interface RenameSetEvent {
|
||||
type: 'rename-set';
|
||||
setId: string;
|
||||
newName: string;
|
||||
}
|
||||
|
||||
export interface RenameTokenEvent {
|
||||
type: 'rename-token';
|
||||
setId: string;
|
||||
tokenId: string;
|
||||
newName: string;
|
||||
}
|
||||
|
||||
export interface DeleteThemeEvent {
|
||||
type: 'delete-theme';
|
||||
themeId: string;
|
||||
}
|
||||
|
||||
export interface DeleteSetEvent {
|
||||
type: 'delete-set';
|
||||
setId: string;
|
||||
}
|
||||
|
||||
export interface DeleteTokenEvent {
|
||||
type: 'delete-token';
|
||||
setId: string;
|
||||
tokenId: string;
|
||||
}
|
||||
|
||||
export interface ToggleThemeEvent {
|
||||
type: 'toggle-theme';
|
||||
themeId: string;
|
||||
}
|
||||
|
||||
export interface ToggleSetEvent {
|
||||
type: 'toggle-set';
|
||||
setId: string;
|
||||
}
|
||||
|
||||
export interface ApplyTokenEvent {
|
||||
type: 'apply-token';
|
||||
setId: string;
|
||||
tokenId: string;
|
||||
attributes?: TokenProperty[];
|
||||
}
|
||||
|
||||
export type PluginUIEvent =
|
||||
| LoadLibraryEvent
|
||||
| LoadTokensEvent
|
||||
| AddThemeEvent
|
||||
| AddSetEvent
|
||||
| AddTokenEvent
|
||||
| RenameThemeEvent
|
||||
| RenameSetEvent
|
||||
| RenameTokenEvent
|
||||
| DeleteThemeEvent
|
||||
| DeleteSetEvent
|
||||
| DeleteTokenEvent
|
||||
| ToggleThemeEvent
|
||||
| ToggleSetEvent
|
||||
| ApplyTokenEvent;
|
||||
|
||||
// Events sent from the plugin to the ui
|
||||
|
||||
export interface ThemePluginEvent {
|
||||
type: 'theme';
|
||||
content: string;
|
||||
}
|
||||
|
||||
export type PluginMessageEvent = ThemePluginEvent;
|
||||
246
plugins/apps/poc-tokens-plugin/src/plugin.ts
Normal file
246
plugins/apps/poc-tokens-plugin/src/plugin.ts
Normal file
@@ -0,0 +1,246 @@
|
||||
import type { PluginMessageEvent, PluginUIEvent } from './model.js';
|
||||
import { TokenType, TokenProperty } from '@penpot/plugin-types';
|
||||
|
||||
penpot.ui.open('Design Tokens test', `?theme=${penpot.theme}`, {
|
||||
width: 1000,
|
||||
height: 800,
|
||||
});
|
||||
|
||||
penpot.on('themechange', (theme) => {
|
||||
sendMessage({ type: 'theme', content: theme });
|
||||
});
|
||||
|
||||
penpot.ui.onMessage<PluginUIEvent>(async (message) => {
|
||||
if (message.type === 'load-library') {
|
||||
loadLibrary();
|
||||
} else if (message.type === 'load-tokens') {
|
||||
loadTokens(message.setId);
|
||||
} else if (message.type === 'add-theme') {
|
||||
addTheme(message.themeGroup, message.themeName);
|
||||
} else if (message.type === 'add-set') {
|
||||
addSet(message.setName);
|
||||
} else if (message.type === 'add-token') {
|
||||
addToken(
|
||||
message.setId,
|
||||
message.tokenType,
|
||||
message.tokenName,
|
||||
message.tokenValue,
|
||||
);
|
||||
} else if (message.type === 'rename-theme') {
|
||||
renameTheme(message.themeId, message.newName);
|
||||
} else if (message.type === 'rename-set') {
|
||||
renameSet(message.setId, message.newName);
|
||||
} else if (message.type === 'rename-token') {
|
||||
renameToken(message.setId, message.tokenId, message.newName);
|
||||
} else if (message.type === 'delete-theme') {
|
||||
deleteTheme(message.themeId);
|
||||
} else if (message.type === 'delete-set') {
|
||||
deleteSet(message.setId);
|
||||
} else if (message.type === 'delete-token') {
|
||||
deleteToken(message.setId, message.tokenId);
|
||||
} else if (message.type === 'toggle-theme') {
|
||||
toggleTheme(message.themeId);
|
||||
} else if (message.type === 'toggle-set') {
|
||||
toggleSet(message.setId);
|
||||
} else if (message.type === 'apply-token') {
|
||||
applyToken(message.setId, message.tokenId, message.attributes);
|
||||
}
|
||||
});
|
||||
|
||||
function sendMessage(message: PluginMessageEvent) {
|
||||
penpot.ui.sendMessage(message);
|
||||
}
|
||||
|
||||
function loadLibrary() {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
|
||||
const themes = tokensCatalog.themes;
|
||||
|
||||
const themesData = themes.map((theme) => {
|
||||
return {
|
||||
id: theme.id,
|
||||
group: theme.group,
|
||||
name: theme.name,
|
||||
active: theme.active,
|
||||
};
|
||||
});
|
||||
|
||||
penpot.ui.sendMessage({
|
||||
source: 'penpot',
|
||||
type: 'set-themes',
|
||||
themesData,
|
||||
});
|
||||
|
||||
const sets = tokensCatalog.sets;
|
||||
|
||||
const setsData = sets.map((set) => {
|
||||
return {
|
||||
id: set.id,
|
||||
name: set.name,
|
||||
active: set.active,
|
||||
};
|
||||
});
|
||||
|
||||
penpot.ui.sendMessage({
|
||||
source: 'penpot',
|
||||
type: 'set-sets',
|
||||
setsData,
|
||||
});
|
||||
}
|
||||
|
||||
function loadTokens(setId: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const set = tokensCatalog?.getSetById(setId);
|
||||
const tokensByType = set?.tokensByType;
|
||||
|
||||
const tokenGroupsData = [];
|
||||
if (tokensByType) {
|
||||
for (const group of tokensByType) {
|
||||
const type = group[0];
|
||||
const tokens = group[1];
|
||||
tokenGroupsData.push([
|
||||
type,
|
||||
tokens.map((token) => {
|
||||
return {
|
||||
id: token.id,
|
||||
name: token.name,
|
||||
description: token.description,
|
||||
};
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
penpot.ui.sendMessage({
|
||||
source: 'penpot',
|
||||
type: 'set-tokens',
|
||||
tokenGroupsData,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function addTheme(themeGroup: string, themeName: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const theme = tokensCatalog?.addTheme(themeGroup, themeName);
|
||||
if (theme) {
|
||||
loadLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
function addSet(setName: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const set = tokensCatalog?.addSet(setName);
|
||||
if (set) {
|
||||
loadLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
function addToken(
|
||||
setId: string,
|
||||
tokenType: string,
|
||||
tokenName: string,
|
||||
tokenValue: unknown,
|
||||
) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const set = tokensCatalog?.getSetById(setId);
|
||||
const token = set?.addToken(tokenType as TokenType, tokenName, tokenValue);
|
||||
if (token) {
|
||||
loadTokens(setId);
|
||||
}
|
||||
}
|
||||
|
||||
function renameTheme(themeId: string, newName: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const theme = tokensCatalog?.getThemeById(themeId);
|
||||
if (theme) {
|
||||
theme.name = newName;
|
||||
loadLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
function renameSet(setId: string, newName: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const set = tokensCatalog?.getSetById(setId);
|
||||
if (set) {
|
||||
set.name = newName;
|
||||
loadLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
function renameToken(setId: string, tokenId: string, newName: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const set = tokensCatalog?.getSetById(setId);
|
||||
const token = set?.getTokenById(tokenId);
|
||||
if (token) {
|
||||
token.name = newName;
|
||||
loadTokens(setId);
|
||||
}
|
||||
}
|
||||
|
||||
function deleteTheme(themeId: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const theme = tokensCatalog?.getThemeById(themeId);
|
||||
if (theme) {
|
||||
theme.remove();
|
||||
loadLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
function deleteSet(setId: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const set = tokensCatalog?.getSetById(setId);
|
||||
if (set) {
|
||||
set.remove();
|
||||
loadLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
function deleteToken(setId: string, tokenId: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const set = tokensCatalog?.getSetById(setId);
|
||||
const token = set?.getTokenById(tokenId);
|
||||
if (token) {
|
||||
token.remove();
|
||||
loadTokens(setId);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleTheme(themeId: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const theme = tokensCatalog?.getThemeById(themeId);
|
||||
if (theme) {
|
||||
theme.toggleActive();
|
||||
loadLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSet(setId: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const set = tokensCatalog?.getSetById(setId);
|
||||
if (set) {
|
||||
set.toggleActive();
|
||||
loadLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
function applyToken(
|
||||
setId: string,
|
||||
tokenId: string,
|
||||
attributes: TokenProperty[] | undefined,
|
||||
) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const set = tokensCatalog?.getSetById(setId);
|
||||
const token = set?.getTokenById(tokenId);
|
||||
|
||||
if (token) {
|
||||
token.applyToSelected(attributes);
|
||||
}
|
||||
|
||||
// Alternatve way
|
||||
//
|
||||
// const selection = penpot.selection;
|
||||
// if (token && selection) {
|
||||
// for (const shape of selection) {
|
||||
// shape.applyToken(token, attributes);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
23
plugins/apps/poc-tokens-plugin/src/styles.css
Normal file
23
plugins/apps/poc-tokens-plugin/src/styles.css
Normal file
@@ -0,0 +1,23 @@
|
||||
/* @import "@penpot/plugin-styles/styles.css"; */
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
line-height: 1.5;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-block-start: var(--spacing-12);
|
||||
}
|
||||
|
||||
.title-l {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.headline-l {
|
||||
margin-block-start: var(--spacing-8);
|
||||
}
|
||||
10
plugins/apps/poc-tokens-plugin/tsconfig.app.json
Normal file
10
plugins/apps/poc-tokens-plugin/tsconfig.app.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": []
|
||||
},
|
||||
"files": ["src/main.ts"],
|
||||
"include": ["src/**/*.d.ts"],
|
||||
"exclude": ["src/**/*.test.ts", "src/**/*.spec.ts"]
|
||||
}
|
||||
7
plugins/apps/poc-tokens-plugin/tsconfig.editor.json
Normal file
7
plugins/apps/poc-tokens-plugin/tsconfig.editor.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": ["src/**/*.ts"],
|
||||
"compilerOptions": {
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
||||
33
plugins/apps/poc-tokens-plugin/tsconfig.json
Normal file
33
plugins/apps/poc-tokens-plugin/tsconfig.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2022",
|
||||
"useDefineForClassFields": false,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.editor.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.plugin.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
||||
8
plugins/apps/poc-tokens-plugin/tsconfig.plugin.json
Normal file
8
plugins/apps/poc-tokens-plugin/tsconfig.plugin.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"types": []
|
||||
},
|
||||
"files": ["src/plugin.ts"],
|
||||
"include": ["../../libs/plugin-types/index.d.ts"]
|
||||
}
|
||||
1078
plugins/libs/plugin-types/index.d.ts
vendored
1078
plugins/libs/plugin-types/index.d.ts
vendored
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@
|
||||
"start:plugin:table": "nx run table-plugin:init",
|
||||
"start:plugin:renamelayers": "nx run rename-layers-plugin:init",
|
||||
"start:plugin:colors-to-tokens": "nx run colors-to-tokens-plugin:init",
|
||||
"start:plugin:poc-tokens": "nx run poc-tokens-plugin:init",
|
||||
"build": "nx build plugins-runtime --emptyOutDir=true",
|
||||
"build:plugins": "nx run-many -t build --parallel -p tag:type:plugin --exclude=poc-state-plugin",
|
||||
"build:styles-example": "nx run example-styles:build",
|
||||
|
||||
Reference in New Issue
Block a user