Repository: penpot/penpot
File: frontend/src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs (ClojureScript)

Code before:
;; The color input exists in two variants: the normal (primitive) input and the
;; indexed input. Both support hex, rgb(), hsl(), named colors, opacity changes,
;; color-picker selection, and token references.
;;
;; 1) Normal Color Input (primitive)
;;    - Used when the token’s `:value` *is a single color* or a reference.
;;    - Writes directly to `:value`.
;;    - Validation ensures the color is in an accepted format or is a valid
;;      color token reference.

;; 2) Indexed Color Input
;;    - Used when the token’s value stores an array of items (e.g. inside
;;      shadows, where each shadow layer has its own :color field).
;;    - The input writes to a nested value-subfield:
;;         [:value <value-subfield> <index> :color]
;;    - Only that specific color entry is validated.
;;    - Other properties (offsets, blur, inset, etc.) remain untouched.
;;
;; Both variants provide identical color-picker and text-input behavior, but
;; differ in how they persist the value within the form’s nested structure.

(defn- resolve-value
  [tokens prev-token token-name value]
  (let [valid-token-name?
        (and (string? token-name)
             (m/validate cto/token-name-ref token-name))

        token
        {:value value
         :name (if (or (not valid-token-name?) (str/blank? token-name))
                 "__PENPOT__TOKEN__NAME__PLACEHOLDER__"
                 token-name)}

        tokens
        (-> tokens
            ;; Remove previous token when renaming a token
            (dissoc (:name prev-token))
            (update (:name token) #(ctob/make-token (merge % prev-token token))))]

    (->> tokens
         (sd/resolve-tokens-interactive)
         (rx/mapcat
          (fn [resolved-tokens]
            (let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))]
              (if resolved-value
                (rx/of {:value resolved-value})
                (rx/of {:error (first errors)}))))))))

(defn- hex->color-obj
  [hex]
  (when-let [tc (tinycolor/valid-color hex)]

Review comment (refactor):
We should not use malli directly here, we already have `app.common.schema` namespace that exposes that validation functions.

Also, I think we should extract the regex and use it directly instead of using schema validation there.

Code after:
;; indexed input. Both support hex, rgb(), hsl(), named colors, opacity changes,
;; color-picker selection, and token references.
;;
;; 1) Normal Color Input (primitive)
;;    - Used when the token’s `:value` *is a single color* or a reference.
;;    - Writes directly to `:value`.
;;    - Validation ensures the color is in an accepted format or is a valid
;;      color token reference.

;; 2) Indexed Color Input
;;    - Used when the token’s value stores an array of items (e.g. inside
;;      shadows, where each shadow layer has its own :color field).
;;    - The input writes to a nested value-subfield:
;;         [:value <value-subfield> <index> :color]
;;    - Only that specific color entry is validated.
;;    - Other properties (offsets, blur, inset, etc.) remain untouched.
;;
;; Both variants provide identical color-picker and text-input behavior, but
;; differ in how they persist the value within the form’s nested structure.

(defn- resolve-value
  [tokens prev-token token-name value]
  (let [valid-token-name?
        (and (string? token-name)
             (re-matches  cto/token-name-validation-regex token-name))

        token
        {:value value
         :name (if (or (not valid-token-name?) (str/blank? token-name))
                 "__PENPOT__TOKEN__NAME__PLACEHOLDER__"
                 token-name)}

        tokens
        (-> tokens
            ;; Remove previous token when renaming a token
            (dissoc (:name prev-token))
            (update (:name token) #(ctob/make-token (merge % prev-token token))))]

    (->> tokens
         (sd/resolve-tokens-interactive)
         (rx/mapcat
          (fn [resolved-tokens]
            (let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))]
              (if resolved-value
                (rx/of {:value resolved-value})
                (rx/of {:error (first errors)}))))))))

(defn- hex->color-obj
  [hex]
  (when-let [tc (tinycolor/valid-color hex)]
    (let [hex (tinycolor/->hex-string tc)