🔧 Add tokenscript flag and more validations to token values

This commit is contained in:
Andrés Moya 2026-02-18 10:40:08 +01:00 committed by Andrés Moya
parent 72a855d4ac
commit ba87ea1a44
16 changed files with 170 additions and 72 deletions

View File

@ -760,6 +760,21 @@
default default
v)))) v))))
(defn percent?
[v]
(str/numeric? (str/rtrim v "%")))
(defn parse-percent
([v]
(parse-percent v nil))
([v default]
(if (str/ends-with? v "%")
(let [v (impl-parse-double (str/trim v "%"))]
(if (or (nil? v) (nan? v))
default
(/ v 100)))
(parse-double v default))))
(defn parse-uuid (defn parse-uuid
[v] [v]
(try (try

View File

@ -31,18 +31,56 @@
(def schema:token-value-generic (def schema:token-value-generic
[::sm/text {:error/fn token-value-empty-fn}]) [::sm/text {:error/fn token-value-empty-fn}])
(def schema:token-value-numeric
[:and
[::sm/text {:error/fn token-value-empty-fn}]
[:fn {:error/fn #(tr "workspace.tokens.invalid-value" (:value %))}
(fn [value]
(if (str/numeric? value)
(let [n (d/parse-double value)]
(some? n))
true))]]) ;; Leave references or formulas to be checked by the resolver
(def schema:token-value-percent
[:and
[::sm/text {:error/fn token-value-empty-fn}]
[:fn {:error/fn #(tr "workspace.tokens.value-with-percent" (:value %))}
(fn [value]
(if (d/percent? value)
(let [v (d/parse-percent value)]
(some? v))
true))]]) ;; Leave references or formulas to be checked by the resolver
(def schema:token-value-composite-ref (def schema:token-value-composite-ref
[::sm/text {:error/fn token-value-empty-fn}]) [::sm/text {:error/fn token-value-empty-fn}])
(def schema:token-value-opacity
[:and
[::sm/text {:error/fn token-value-empty-fn}]
[:fn {:error/fn #(tr "workspace.tokens.opacity-range")}
(fn [opacity]
(if (str/numeric? opacity)
(let [n (d/parse-percent opacity)]
(and (some? n) (<= 0 n 1)))
true))]]) ;; Leave references or formulas to be checked by the resolver
(def schema:token-value-font-family (def schema:token-value-font-family
[:vector ::sm/text]) [:or
[:vector ::sm/text]
cto/schema:token-ref])
(def schema:token-value-font-weight
[:or
[:fn {:error/fn #(tr "workspace.tokens.invalid-font-weight-token-value")}
cto/valid-font-weight-variant]
::sm/text]) ;; Leave references or formulas to be checked by the resolver
(def schema:token-value-typography-map (def schema:token-value-typography-map
[:map [:map
[:font-family {:optional true} schema:token-value-font-family] [:font-family {:optional true} schema:token-value-font-family]
[:font-weight {:optional true} schema:token-value-generic] [:font-size {:optional true} schema:token-value-numeric]
[:font-size {:optional true} schema:token-value-generic] [:font-weight {:optional true} schema:token-value-font-weight]
[:line-height {:optional true} schema:token-value-generic] [:line-height {:optional true} schema:token-value-percent]
[:letter-spacing {:optional true} schema:token-value-generic] [:letter-spacing {:optional true} schema:token-value-generic]
[:paragraph-spacing {:optional true} schema:token-value-generic] [:paragraph-spacing {:optional true} schema:token-value-generic]
[:text-decoration {:optional true} schema:token-value-generic] [:text-decoration {:optional true} schema:token-value-generic]
@ -84,7 +122,10 @@
[token-type] [token-type]
[:multi {:dispatch (constantly token-type) [:multi {:dispatch (constantly token-type)
:title "Token Value"} :title "Token Value"}
[:opacity schema:token-value-opacity]
[:font-family schema:token-value-font-family] [:font-family schema:token-value-font-family]
[:font-size schema:token-value-numeric]
[:font-weight schema:token-value-font-weight]
[:typography schema:token-value-typography] [:typography schema:token-value-typography]
[:shadow schema:token-value-shadow] [:shadow schema:token-value-shadow]
[::m/default schema:token-value-generic]]) [::m/default schema:token-value-generic]])
@ -169,7 +210,7 @@
[tokens-lib set-id] [tokens-lib set-id]
[:and [:and
[:string {:min 1 :max 255 :error/fn #(str (:value %) (tr "workspace.tokens.token-name-length-validation-error"))}] [:string {:min 1 :max 255 :error/fn #(str (:value %) (tr "workspace.tokens.token-name-length-validation-error"))}]
[:fn {:error/fn #(tr "errors.token-set-already-exists" (:value %))} [:fn {:error/fn #(tr "errors.token-set-already-exists")}
(fn [name] (fn [name]
(or (nil? tokens-lib) (or (nil? tokens-lib)
(let [set (ctob/get-set-by-name tokens-lib name)] (let [set (ctob/get-set-by-name tokens-lib name)]
@ -196,7 +237,7 @@
[tokens-lib name theme-id] [tokens-lib name theme-id]
[:and [:and
[:string {:min 0 :max 255 :error/fn #(str (:value %) (tr "workspace.tokens.token-name-length-validation-error"))}] [:string {:min 0 :max 255 :error/fn #(str (:value %) (tr "workspace.tokens.token-name-length-validation-error"))}]
[:fn {:error/fn #(tr "errors.token-theme-already-exists" (:value %))} [:fn {:error/fn #(tr "errors.token-theme-already-exists")}
(fn [group] (fn [group]
(or (nil? tokens-lib) (or (nil? tokens-lib)
(let [theme (ctob/get-theme-by-name tokens-lib group name)] (let [theme (ctob/get-theme-by-name tokens-lib group name)]

View File

@ -143,6 +143,15 @@
:gen/gen sg/text} :gen/gen sg/text}
token-name-validation-regex]) token-name-validation-regex])
(def token-ref-validation-regex
#"^\{[a-zA-Z0-9_-][a-zA-Z0-9$_-]*(\.[a-zA-Z0-9$_-]+)*\}$")
(def schema:token-ref
"A token reference is a token name enclosed in {}."
[:re {:title "TokenRef"
:gen/gen sg/text}
token-ref-validation-regex])
(def schema:token-type (def schema:token-type
[::sm/one-of {:decode/json (fn [type] [::sm/one-of {:decode/json (fn [type]
(if (string? type) (if (string? type)

View File

@ -69,6 +69,10 @@
(and (number-with-unit-symbol? v) (and (number-with-unit-symbol? v)
(= (.-unit v) "rem"))) (= (.-unit v) "rem")))
(defn percent-number-with-unit? [v]
(and (number-with-unit-symbol? v)
(= (.-unit v) "%")))
(defn rem->px [^js v] (defn rem->px [^js v]
(* (.-value v) 16)) (* (.-value v) 16))
@ -87,10 +91,12 @@
(defn tokenscript-symbols->penpot-unit [^js v] (defn tokenscript-symbols->penpot-unit [^js v]
(cond (cond
(nil? v) nil
(structured-token? v) (structured-token->penpot-map v) (structured-token? v) (structured-token->penpot-map v)
(list-symbol? v) (structured-token->penpot-map v) (list-symbol? v) (structured-token->penpot-map v)
(color-symbol? v) (.-value (.to v "hex")) (color-symbol? v) (.-value (.to v "hex"))
(rem-number-with-unit? v) (rem->px v) (rem-number-with-unit? v) (rem->px v)
(percent-number-with-unit? v) (/ (.-value v) 100)
:else (.-value v))) :else (.-value v)))
;; Processors ------------------------------------------------------------------ ;; Processors ------------------------------------------------------------------

View File

@ -8,10 +8,11 @@
(:require (:require
[app.common.json :as json] [app.common.json :as json]
[app.common.path-names :as cpn] [app.common.path-names :as cpn]
[app.common.types.tokens-lib :as ctob] [app.common.types.tokens-lib :as ctob]
[app.config :as cf]
[app.main.data.notifications :as ntf] [app.main.data.notifications :as ntf]
[app.main.data.style-dictionary :as sd] [app.main.data.style-dictionary :as sd]
[app.main.data.tokenscript :as ts]
[app.main.data.workspace.tokens.errors :as wte] [app.main.data.workspace.tokens.errors :as wte]
[app.main.store :as st] [app.main.store :as st]
[app.util.i18n :refer [tr]] [app.util.i18n :refer [tr]]
@ -74,15 +75,18 @@
(when unknown-tokens (when unknown-tokens
(st/emit! (show-unknown-types-warning unknown-tokens))) (st/emit! (show-unknown-types-warning unknown-tokens)))
(try (try
(->> (ctob/get-all-tokens-map tokens-lib) (let [tokens-tree (ctob/get-all-tokens-map tokens-lib)
(sd/resolve-tokens-with-verbose-errors) resolved-tokens (if (contains? cf/flags :tokenscript)
(rx/map (fn [_] (rx/of (ts/resolve-tokens tokens-tree))
tokens-lib)) (sd/resolve-tokens-with-verbose-errors tokens-tree))]
(rx/catch (fn [sd-error] (->> resolved-tokens
(let [reference-errors (extract-reference-errors sd-error)] (rx/map (fn [_]
(if reference-errors tokens-lib))
(rx/of tokens-lib) (rx/catch (fn [sd-error]
(throw (wte/error-ex-info :error.import/style-dictionary-unknown-error sd-error sd-error))))))) (let [reference-errors (extract-reference-errors sd-error)]
(if reference-errors
(rx/of tokens-lib)
(throw (wte/error-ex-info :error.import/style-dictionary-unknown-error sd-error sd-error))))))))
(catch js/Error e (catch js/Error e
(throw (wte/error-ex-info :error.import/style-dictionary-unknown-error "" e))))) (throw (wte/error-ex-info :error.import/style-dictionary-unknown-error "" e)))))

View File

@ -6,13 +6,16 @@
(ns app.main.data.workspace.tokens.propagation (ns app.main.data.workspace.tokens.propagation
(:require (:require
[app.common.data :as d]
[app.common.files.helpers :as cfh] [app.common.files.helpers :as cfh]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.time :as ct] [app.common.time :as ct]
[app.common.types.token :as ctt] [app.common.types.token :as ctt]
[app.common.types.tokens-lib :as ctob] [app.common.types.tokens-lib :as ctob]
[app.config :as cf]
[app.main.data.helpers :as dsh] [app.main.data.helpers :as dsh]
[app.main.data.style-dictionary :as sd] [app.main.data.style-dictionary :as sd]
[app.main.data.tokenscript :as ts]
[app.main.data.workspace.shapes :as dwsh] [app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.thumbnails :as dwt] [app.main.data.workspace.thumbnails :as dwt]
[app.main.data.workspace.tokens.application :as dwta] [app.main.data.workspace.tokens.application :as dwta]
@ -210,10 +213,13 @@
(ptk/reify ::propagate-workspace-tokens (ptk/reify ::propagate-workspace-tokens
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(when-let [tokens-lib (-> (dsh/lookup-file-data state) (when-let [tokens-tree (-> (dsh/lookup-file-data state)
(get :tokens-lib))] (get :tokens-lib)
(->> (ctob/get-tokens-in-active-sets tokens-lib) (ctob/get-tokens-in-active-sets))]
(sd/resolve-tokens) (->> (if (contains? cf/flags :tokenscript)
(rx/of (-> (ts/resolve-tokens tokens-tree)
(d/update-vals #(update % :resolved-value ts/tokenscript-symbols->penpot-unit))))
(sd/resolve-tokens tokens-tree))
(rx/mapcat (fn [sd-tokens] (rx/mapcat (fn [sd-tokens]
(let [undo-id (js/Symbol)] (let [undo-id (js/Symbol)]
(rx/concat (rx/concat

View File

@ -143,8 +143,7 @@
(let [token-ids (set tokens-in-path-ids) (let [token-ids (set tokens-in-path-ids)
remaining-tokens (filter (fn [token] remaining-tokens (filter (fn [token]
(not (contains? token-ids (:id token)))) (not (contains? token-ids (:id token))))
selected-token-set-tokens) selected-token-set-tokens)]
_ (prn "Remaining tokens:" remaining-tokens)]
(seq remaining-tokens)))) (seq remaining-tokens))))
delete-token delete-token

View File

@ -13,8 +13,10 @@
[app.common.types.color :as cl] [app.common.types.color :as cl]
[app.common.types.token :as cto] [app.common.types.token :as cto]
[app.common.types.tokens-lib :as ctob] [app.common.types.tokens-lib :as ctob]
[app.config :as cf]
[app.main.data.style-dictionary :as sd] [app.main.data.style-dictionary :as sd]
[app.main.data.tinycolor :as tinycolor] [app.main.data.tinycolor :as tinycolor]
[app.main.data.tokenscript :as ts]
[app.main.data.workspace.tokens.format :as dwtf] [app.main.data.workspace.tokens.format :as dwtf]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.ui.ds.controls.input :as ds] [app.main.ui.ds.controls.input :as ds]
@ -70,11 +72,15 @@
(dissoc (:name prev-token)) (dissoc (:name prev-token))
(update (:name token) #(ctob/make-token (merge % prev-token token))))] (update (:name token) #(ctob/make-token (merge % prev-token token))))]
(->> tokens (->> (if (contains? cf/flags :tokenscript)
(sd/resolve-tokens-interactive) (rx/of (ts/resolve-tokens tokens))
(sd/resolve-tokens-interactive tokens))
(rx/mapcat (rx/mapcat
(fn [resolved-tokens] (fn [resolved-tokens]
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))] (let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))
resolved-value (if (contains? cf/flags :tokenscript)
(ts/tokenscript-symbols->penpot-unit resolved-value)
resolved-value)]
(if resolved-value (if resolved-value
(rx/of {:value resolved-value}) (rx/of {:value resolved-value})
(rx/of {:error (first errors)})))))))) (rx/of {:error (first errors)}))))))))

View File

@ -10,7 +10,9 @@
[app.common.data :as d] [app.common.data :as d]
[app.common.types.token :as cto] [app.common.types.token :as cto]
[app.common.types.tokens-lib :as ctob] [app.common.types.tokens-lib :as ctob]
[app.config :as cf]
[app.main.data.style-dictionary :as sd] [app.main.data.style-dictionary :as sd]
[app.main.data.tokenscript :as ts]
[app.main.fonts :as fonts] [app.main.fonts :as fonts]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.controls.input :refer [input*]] [app.main.ui.ds.controls.input :refer [input*]]
@ -49,28 +51,30 @@
;; validate data within the form state. ;; validate data within the form state.
(defn- resolve-value (defn- resolve-value
[tokens prev-token token-name value] [tokens prev-token _token-name value]
(let [valid-token-name? (let [tmp-value (cto/split-font-family value)
(and (string? token-name) tmp-name "__PENPOT__FONT_FAMILY__PLACEHOLDER__"
(re-matches cto/token-name-validation-regex token-name))
;; Create a temporary font-family token to validate the value
token token
{:value (cto/split-font-family value) {:name tmp-name
:name (if (or (not valid-token-name?) (str/blank? token-name)) :type :font-family
"__PENPOT__TOKEN__NAME__PLACEHOLDER__" :value (if (= (:type prev-token) :typography)
token-name)} (assoc (:value prev-token) :font-family tmp-value)
tmp-value)}
tokens tokens
(-> tokens (update tokens (:name token) #(ctob/make-token (merge % prev-token token)))]
;; Remove previous token when renaming a token
(dissoc (:name prev-token))
(update (:name token) #(ctob/make-token (merge % prev-token token))))]
(->> tokens (->> (if (contains? cf/flags :tokenscript)
(sd/resolve-tokens-interactive) (rx/of (ts/resolve-tokens tokens))
(sd/resolve-tokens-interactive tokens))
(rx/mapcat (rx/mapcat
(fn [resolved-tokens] (fn [resolved-tokens]
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))] (let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))
resolved-value (if (contains? cf/flags :tokenscript)
(ts/tokenscript-symbols->penpot-unit resolved-value)
resolved-value)]
(if resolved-value (if resolved-value
(rx/of {:value resolved-value}) (rx/of {:value resolved-value})
(rx/of {:error (first errors)})))))))) (rx/of {:error (first errors)}))))))))
@ -176,7 +180,6 @@
(let [message (tr "workspace.tokens.resolved-value" value)] (let [message (tr "workspace.tokens.resolved-value" value)]
(swap! form update :extra-errors dissoc input-name) (swap! form update :extra-errors dissoc input-name)
(reset! hint* {:message message :type "hint"})))))))] (reset! hint* {:message message :type "hint"})))))))]
(fn [] (fn []
(rx/dispose! subs)))) (rx/dispose! subs))))

View File

@ -175,7 +175,10 @@
(sd/resolve-tokens-interactive) (sd/resolve-tokens-interactive)
(rx/mapcat (rx/mapcat
(fn [resolved-tokens] (fn [resolved-tokens]
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))] (let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))
resolved-value (if (contains? cf/flags :tokenscript)
(ts/tokenscript-symbols->penpot-unit resolved-value)
resolved-value)]
(if resolved-value (if resolved-value
(rx/of {:value resolved-value}) (rx/of {:value resolved-value})
(rx/of {:error (first errors)})))))))) (rx/of {:error (first errors)}))))))))

View File

@ -36,9 +36,9 @@
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(defn get-value-for-validator (defn get-value-for-validator
[active-tab value value-subfield form-type] [active-tab value value-subfield value-type]
(case form-type (case value-type
:indexed :indexed
(if (= active-tab :reference) (if (= active-tab :reference)
(:reference value) (:reference value)
@ -62,7 +62,7 @@
make-schema make-schema
input-component input-component
initial initial
type value-type
value-subfield value-subfield
input-value-placeholder] :as props}] input-value-placeholder] :as props}]
@ -178,13 +178,13 @@
on-submit on-submit
(mf/use-fn (mf/use-fn
(mf/deps validate-token token tokens token-type value-subfield type active-tab on-remap-token on-rename-token is-create) (mf/deps validate-token token tokens token-type value-subfield value-type active-tab on-remap-token on-rename-token is-create)
(fn [form _event] (fn [form _event]
(let [name (get-in @form [:clean-data :name]) (let [name (get-in @form [:clean-data :name])
path (str (d/name token-type) "." name) path (str (d/name token-type) "." name)
description (get-in @form [:clean-data :description]) description (get-in @form [:clean-data :description])
value (get-in @form [:clean-data :value]) value (get-in @form [:clean-data :value])
value-for-validation (get-value-for-validator active-tab value value-subfield type)] value-for-validation (get-value-for-validator active-tab value value-subfield value-type)]
(->> (validate-token {:token-value value-for-validation (->> (validate-token {:token-value value-for-validation
:token-name name :token-name name
:token-description description :token-description description
@ -245,7 +245,7 @@
:auto-focus true}]] :auto-focus true}]]
[:div {:class (stl/css :input-row)} [:div {:class (stl/css :input-row)}
(case type (case value-type
:indexed :indexed
[:> input-component [:> input-component
{:token token {:token token

View File

@ -365,7 +365,7 @@
:token-type token-type :token-type token-type
:initial initial :initial initial
:make-schema make-schema :make-schema make-schema
:type :indexed :value-type :indexed
:value-subfield :shadow :value-subfield :shadow
:input-component tabs-wrapper* :input-component tabs-wrapper*
:validator validate-shadow-token})] :validator validate-shadow-token})]

View File

@ -300,6 +300,6 @@
:make-schema make-schema :make-schema make-schema
:token token :token token
:validator validate-typography-token :validator validate-typography-token
:type :composite :value-type :composite
:input-component tabs-wrapper*})] :input-component tabs-wrapper*})]
[:> generic/form* props])) [:> generic/form* props]))

View File

@ -4,7 +4,9 @@
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.types.token :as cto] [app.common.types.token :as cto]
[app.common.types.tokens-lib :as ctob] [app.common.types.tokens-lib :as ctob]
[app.config :as cf]
[app.main.data.style-dictionary :as sd] [app.main.data.style-dictionary :as sd]
[app.main.data.tokenscript :as ts]
[app.main.data.workspace.tokens.errors :as wte] [app.main.data.workspace.tokens.errors :as wte]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[cuerdas.core :as str])) [cuerdas.core :as str]))
@ -36,14 +38,20 @@
:always :always
(update (:name token) #(ctob/make-token (merge % prev-token token))))] (update (:name token) #(ctob/make-token (merge % prev-token token))))]
(->> tokens'
(sd/resolve-tokens-interactive) (->> (if (contains? cf/flags :tokenscript)
(rx/of (ts/resolve-tokens tokens'))
(sd/resolve-tokens-interactive tokens'))
(rx/mapcat (rx/mapcat
(fn [resolved-tokens] (fn [resolved-tokens]
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))] (let [resolved-token (cond-> (get resolved-tokens (:name token))
(contains? cf/flags :tokenscript)
(update :resolved-value ts/tokenscript-symbols->penpot-unit))]
(cond (cond
resolved-value (rx/of resolved-token) (:resolved-value resolved-token)
:else (rx/throw {:errors (or (seq errors) (rx/of resolved-token)
:else (rx/throw {:errors (or (seq (:errors resolved-token))
[(wte/get-error-code :error/unknown-error)])})))))))) [(wte/get-error-code :error/unknown-error)])}))))))))
(defn- validate-token-with [token validators] (defn- validate-token-with [token validators]

View File

@ -176,9 +176,10 @@
(mf/defc token-pill* (mf/defc token-pill*
{::mf/wrap [mf/memo]} {::mf/wrap [mf/memo]}
[{:keys [on-click token on-context-menu selected-shapes is-selected-inside-layout active-theme-tokens]}] [{:keys [on-click token on-context-menu selected-shapes is-selected-inside-layout active-theme-tokens]}]
(let [{:keys [name value errors type]} token (let [{:keys [name value type]} token
resolved-token (get active-theme-tokens (:name token)) resolved-token (get active-theme-tokens (:name token))
errors (:errors resolved-token)
has-selected? (pos? (count selected-shapes)) has-selected? (pos? (count selected-shapes))
is-reference? (cfo/is-reference? token) is-reference? (cfo/is-reference? token)

View File

@ -12,14 +12,12 @@
[app.common.types.token :as cto] [app.common.types.token :as cto]
[app.common.types.tokens-lib :as ctob] [app.common.types.tokens-lib :as ctob]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.main.data.style-dictionary :as sd]
[app.main.data.tokenscript :as ts] [app.main.data.tokenscript :as ts]
[app.main.data.workspace.tokens.application :as dwta] [app.main.data.workspace.tokens.application :as dwta]
[app.main.data.workspace.tokens.library-edit :as dwtl] [app.main.data.workspace.tokens.library-edit :as dwtl]
[app.main.store :as st] [app.main.store :as st]
[app.plugins.utils :as u] [app.plugins.utils :as u]
[app.util.object :as obj] [app.util.object :as obj]
[beicon.v2.core :as rx]
[clojure.datafy :refer [datafy]])) [clojure.datafy :refer [datafy]]))
;; === Token ;; === Token
@ -260,20 +258,19 @@
:decode/options {:key-fn identity} :decode/options {:key-fn identity}
:fn (fn [attrs] :fn (fn [attrs]
(let [tokens-lib (u/locate-tokens-lib file-id) (let [tokens-lib (u/locate-tokens-lib file-id)
tokens-tree (ctob/get-tokens-in-active-sets tokens-lib) token (ctob/make-token attrs)
token (ctob/make-token attrs)] tokens-tree (-> (ctob/get-tokens-in-active-sets tokens-lib)
(->> (assoc tokens-tree (:name token) token) (assoc (:name token) token))
(sd/resolve-tokens-interactive) resolved-tokens (ts/resolve-tokens tokens-tree)
(rx/subs!
(fn [resolved-tokens] {:keys [errors resolved-value] :as resolved-token}
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))] (get resolved-tokens (:name token))]
(if resolved-value
(st/emit! (dwtl/create-token id token)) (if resolved-value
(u/display-not-valid :addToken (str errors))))))) (do (st/emit! (dwtl/create-token id token))
;; TODO: as the addToken function is synchronous, we must return the newly created (token-proxy plugin-id file-id id (:id token)))
;; token even if the validator will throw it away if the resolution fails. (do (u/display-not-valid :addToken (str errors))
;; This will be solved with the TokenScript resolver, that is syncronous. nil))))}
(token-proxy plugin-id file-id id (:id token))))}
:duplicate :duplicate
(fn [] (fn []