diff --git a/docker/devenv/files/nginx.conf b/docker/devenv/files/nginx.conf index 3a0441232e..d2573be684 100644 --- a/docker/devenv/files/nginx.conf +++ b/docker/devenv/files/nginx.conf @@ -223,15 +223,19 @@ http { add_header X-Cache-Status $upstream_cache_status; } - location ~* \.(js|css|jpg|png|svg|ttf|woff|woff2|wasm)$ { + location ~* \.(jpg|png|svg|ttf|woff|woff2)$ { add_header Cache-Control "public, max-age=604800" always; # 7 days } + location ~* \.(js|css|wasm)$ { + add_header Cache-Control "no-store" always; + } + location ~ ^/[^/]+/(.*)$ { return 301 " /404"; } - add_header Cache-Control "no-store, no-cache, max-age=0" always; + add_header Cache-Control "no-store" always; try_files $uri /index.html$is_args$args /index.html =404; } } diff --git a/frontend/deps.edn b/frontend/deps.edn index b9200eb272..eadbd520fe 100644 --- a/frontend/deps.edn +++ b/frontend/deps.edn @@ -50,5 +50,8 @@ :shadow-cljs {:main-opts ["-m" "shadow.cljs.devtools.cli"] - :jvm-opts ["--sun-misc-unsafe-memory-access=allow" "-Dpenpot.wasm.profile-marks=true"]} + :jvm-opts ["--sun-misc-unsafe-memory-access=allow" + "-Dpenpot.wasm.profile-marks=true" + "-XX:+UnlockExperimentalVMOptions" + "-XX:CompileCommand=blackhole,criterium.blackhole.Blackhole::consume"]} }} diff --git a/frontend/scripts/_helpers.js b/frontend/scripts/_helpers.js index 4f658aaf7a..559cacf236 100644 --- a/frontend/scripts/_helpers.js +++ b/frontend/scripts/_helpers.js @@ -181,8 +181,8 @@ export async function watch(baseDir, predicate, callback) { }); } -async function readManifestFile() { - const manifestPath = "resources/public/js/manifest.json"; +async function readManifestFile(resource) { + const manifestPath = "resources/public/" + resource; let content = await fs.readFile(manifestPath, { encoding: "utf8" }); return JSON.parse(content); } diff --git a/frontend/scripts/build b/frontend/scripts/build index 0cd3257702..9d138b9674 100755 --- a/frontend/scripts/build +++ b/frontend/scripts/build @@ -29,7 +29,7 @@ rm -rf target/dist; mkdir -p resources/public; mkdir -p target/dist; -yarn run build:app:main $EXTRA_PARAMS || exit 1 +yarn run build:app:main $EXTRA_PARAMS; if [ "$INCLUDE_WASM" = "yes" ]; then yarn run build:wasm || exit 1; @@ -38,8 +38,6 @@ fi yarn run build:app:libs || exit 1; yarn run build:app:assets || exit 1; -sed -i "s/render-wasm.js/render-wasm.js?version=$CURRENT_VERSION/g" ./resources/public/js/worker/main.js; - rsync -avr resources/public/ target/dist/; if [ "$INCLUDE_STORYBOOK" = "yes" ]; then diff --git a/frontend/shadow-cljs.edn b/frontend/shadow-cljs.edn index 7c52d87cac..75f74bb3f2 100644 --- a/frontend/shadow-cljs.edn +++ b/frontend/shadow-cljs.edn @@ -81,7 +81,7 @@ :source-map-detail-level :all}}} :worker - {:target :esm + {:target :browser :output-dir "resources/public/js/worker/" :asset-path "/js/worker" :devtools {:browser-inject :main @@ -92,6 +92,7 @@ {:main {:entries [app.worker] :web-worker true + :prepend-js "importScripts('/js/worker/render.js');" :depends-on #{}}} :js-options diff --git a/frontend/src/app/config.cljs b/frontend/src/app/config.cljs index 1e1b68d2b2..9e71ac802e 100644 --- a/frontend/src/app/config.cljs +++ b/frontend/src/app/config.cljs @@ -126,7 +126,7 @@ public-uri)) (def worker-uri - (obj/get global "penpotWorkerURI" "/js/worker.js")) + (obj/get global "penpotWorkerURI" "/js/worker/main.js")) (defn external-feature-flag [flag value] @@ -188,6 +188,11 @@ (true? thumbnail?) (u/join (dm/str id "/thumbnail")) (false? thumbnail?) (u/join (dm/str id))))))) -(defn resolve-static-asset - [path] - (u/join public-uri path)) +(defn resolve-href + [resource] + (let [version (get version :full) + href (-> public-uri + (u/ensure-path-slash) + (u/join resource) + (get :path))] + (str href "?version=" version))) diff --git a/frontend/src/app/main/data/style_dictionary.cljs b/frontend/src/app/main/data/style_dictionary.cljs index 3c869e17c2..961c0dbc96 100644 --- a/frontend/src/app/main/data/style_dictionary.cljs +++ b/frontend/src/app/main/data/style_dictionary.cljs @@ -261,14 +261,19 @@ (defn- parse-sd-token-font-family-value [value] - (let [missing-references (seq (some cto/find-token-value-references value))] + (let [value (-> (js->clj value) (flatten)) + valid-font-family (or (string? value) (every? string? value)) + missing-references (seq (some cto/find-token-value-references value))] (cond + (not valid-font-family) + {:errors [(wte/error-with-value :error.style-dictionary/invalid-token-value-font-family value)]} + missing-references {:errors [(wte/error-with-value :error.style-dictionary/missing-reference missing-references)] :references missing-references} :else - {:value (-> (js->clj value) (flatten))}))) + {:value value}))) (defn parse-atomic-typography-value [token-type token-value] (case token-type diff --git a/frontend/src/app/main/data/workspace/tokens/errors.cljs b/frontend/src/app/main/data/workspace/tokens/errors.cljs index ac733c52ac..acc5650e30 100644 --- a/frontend/src/app/main/data/workspace/tokens/errors.cljs +++ b/frontend/src/app/main/data/workspace/tokens/errors.cljs @@ -88,6 +88,10 @@ {:error/code :error.style-dictionary/invalid-token-value-font-weight :error/fn #(tr "workspace.tokens.invalid-font-weight-token-value" %)} + :error.style-dictionary/invalid-token-value-font-family + {:error/code :error.style-dictionary/invalid-token-value-font-family + :error/fn #(tr "workspace.tokens.invalid-font-family-token-value" %)} + :error.style-dictionary/invalid-token-value-typography {:error/code :error.style-dictionary/invalid-token-value-typography :error/fn #(tr "workspace.tokens.invalid-token-value-typography" %)} diff --git a/frontend/src/app/main/ui/exports/assets.cljs b/frontend/src/app/main/ui/exports/assets.cljs index d444b56139..cf97f6e498 100644 --- a/frontend/src/app/main/ui/exports/assets.cljs +++ b/frontend/src/app/main/ui/exports/assets.cljs @@ -223,24 +223,30 @@ circ (* 2 Math/PI 12) pct (- circ (* circ (/ progress total))) - pwidth (if error? - 280 - (/ (* progress 280) total)) - color (cond - error? clr/new-danger - healthy? (if is-default-theme? - clr/new-primary - clr/new-primary-light) - (not healthy?) clr/new-warning) + pwidth + (if error? + 280 + (/ (* progress 280) total)) - background-clr (if is-default-theme? - clr/background-quaternary - clr/background-quaternary-light) - title (cond - error? (tr "workspace.options.exporting-object-error") - complete? (tr "workspace.options.exporting-complete") - healthy? (tr "workspace.options.exporting-object") - (not healthy?) (tr "workspace.options.exporting-object-slow")) + color + (cond + error? clr/new-danger + healthy? (if is-default-theme? + clr/new-primary + clr/new-primary-light) + (not healthy?) clr/new-warning) + + background-clr + (if is-default-theme? + clr/background-quaternary + clr/background-quaternary-light) + + title + (cond + error? (tr "workspace.options.exporting-object-error") + complete? (tr "workspace.options.exporting-complete") + healthy? (tr "workspace.options.exporting-object") + (not healthy?) (tr "workspace.options.exporting-object-slow")) retry-last-export (mf/use-fn #(st/emit! (de/retry-last-export))) @@ -284,7 +290,7 @@ :on-click retry-last-export} (tr "workspace.options.retry")] - [:p {:class (stl/css :progress)} + [:span {:class (stl/css :progress)} (dm/str progress " / " total)])] [:button {:class (stl/css :progress-close-button) diff --git a/frontend/src/app/main/ui/settings/subscription.cljs b/frontend/src/app/main/ui/settings/subscription.cljs index 07c55f22ae..8b1d0422b6 100644 --- a/frontend/src/app/main/ui/settings/subscription.cljs +++ b/frontend/src/app/main/ui/settings/subscription.cljs @@ -77,7 +77,7 @@ [:button {:class (stl/css :cta-button :bottom-link) :on-click cta-link-trial} cta-text-trial])]) -(defn schema:seats-form [min-editors] +(defn- make-management-form-schema [min-editors] [:map {:title "SeatsForm"} [:min-members [::sm/number {:min min-editors :max 9999}]] @@ -87,7 +87,6 @@ {::mf/register modal/components ::mf/register-as :management-dialog} [{:keys [subscription-type current-subscription editors subscribe-to-trial]}] - (let [unlimited-modal-step* (mf/use-state 1) @@ -112,9 +111,12 @@ {:min-members min-editors :redirect-to-payment-details false}) + schema + (mf/with-memo [min-editors] + (make-management-form-schema min-editors)) + form - (fm/use-form :schema (schema:seats-form min-editors) - :initial initial) + (fm/use-form :schema schema :initial initial) submit-in-progress (mf/use-ref false) @@ -334,11 +336,15 @@ [:> raw-svg* {:id (if (= "light" (:theme profile)) "logo-subscription-light" "logo-subscription")}]] [:div {:class (stl/css :modal-end)} - [:div {:class (stl/css :modal-title)} (tr "subscription.settings.sucess.dialog.title" subscription-name)] + [:div {:class (stl/css :modal-title)} + (tr "subscription.settings.sucess.dialog.title" subscription-name)] (when (not= subscription-name "professional") - [:p {:class (stl/css :modal-text-large)} (tr "subscription.settings.success.dialog.thanks" subscription-name)]) - [:p {:class (stl/css :modal-text-large)} (tr "subscription.settings.success.dialog.description")] - [:p {:class (stl/css :modal-text-large)} (tr "subscription.settings.sucess.dialog.footer")] + [:p {:class (stl/css :modal-text-large)} + (tr "subscription.settings.success.dialog.thanks" subscription-name)]) + [:p {:class (stl/css :modal-text-large)} + (tr "subscription.settings.success.dialog.description")] + [:p {:class (stl/css :modal-text-large)} + (tr "subscription.settings.sucess.dialog.footer")] [:div {:class (stl/css :success-action-buttons)} [:input @@ -418,7 +424,11 @@ (mf/with-effect [] (dom/set-html-title (tr "subscription.labels"))) - (mf/with-effect [authenticated? show-subscription-success-modal? show-trial-subscription-modal? success-modal-is-trial? subscription] + (mf/with-effect [authenticated? + show-subscription-success-modal? + show-trial-subscription-modal? + success-modal-is-trial? + subscription] (when ^boolean authenticated? (cond ^boolean show-trial-subscription-modal? diff --git a/frontend/src/app/main/ui/workspace/shapes/text/v2_editor.cljs b/frontend/src/app/main/ui/workspace/shapes/text/v2_editor.cljs index a8086e2bdb..410498e13f 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/v2_editor.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/v2_editor.cljs @@ -333,7 +333,7 @@ (case valign "bottom" (- y (- height selrect-height)) "center" (- y (/ (- height selrect-height) 2)) - "top" y) + y) y)] [(assoc selrect :y y :width max-width :height max-height) transform]) diff --git a/frontend/src/app/main/ui/workspace/top_toolbar.scss b/frontend/src/app/main/ui/workspace/top_toolbar.scss index 53b534569e..d005682878 100644 --- a/frontend/src/app/main/ui/workspace/top_toolbar.scss +++ b/frontend/src/app/main/ui/workspace/top_toolbar.scss @@ -18,7 +18,7 @@ padding: deprecated.$s-8 deprecated.$s-16; border-radius: deprecated.$s-8; border: deprecated.$s-2 solid var(--panel-border-color); - z-index: deprecated.$z-index-3; + z-index: deprecated.$z-index-1; background-color: var(--color-background-primary); transition: top 0.3s, diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index d183dcf14c..32b82196b9 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -1351,47 +1351,13 @@ (defn init-wasm-module [module] (let [default-fn (unchecked-get module "default") - serializers - #js - {:blur-type (unchecked-get module "RawBlurType") - :blend-mode (unchecked-get module "RawBlendMode") - :bool-type (unchecked-get module "RawBoolType") - :font-style (unchecked-get module "RawFontStyle") - :flex-direction (unchecked-get module "RawFlexDirection") - :grid-direction (unchecked-get module "RawGridDirection") - :grow-type (unchecked-get module "RawGrowType") - :align-items (unchecked-get module "RawAlignItems") - :align-self (unchecked-get module "RawAlignSelf") - :align-content (unchecked-get module "RawAlignContent") - :justify-items (unchecked-get module "RawJustifyItems") - :justify-content (unchecked-get module "RawJustifyContent") - :justify-self (unchecked-get module "RawJustifySelf") - :wrap-type (unchecked-get module "RawWrapType") - :grid-track-type (unchecked-get module "RawGridTrackType") - :shadow-style (unchecked-get module "RawShadowStyle") - :stroke-style (unchecked-get module "RawStrokeStyle") - :stroke-cap (unchecked-get module "RawStrokeCap") - :shape-type (unchecked-get module "RawShapeType") - :constraint-h (unchecked-get module "RawConstraintH") - :constraint-v (unchecked-get module "RawConstraintV") - :sizing (unchecked-get module "RawSizing") - :vertical-align (unchecked-get module "RawVerticalAlign") - :fill-data (unchecked-get module "RawFillData") - :text-align (unchecked-get module "RawTextAlign") - :text-direction (unchecked-get module "RawTextDirection") - :text-decoration (unchecked-get module "RawTextDecoration") - :text-transform (unchecked-get module "RawTextTransform") - :segment-data (unchecked-get module "RawSegmentData") - :stroke-linecap (unchecked-get module "RawStrokeLineCap") - :stroke-linejoin (unchecked-get module "RawStrokeLineJoin") - :fill-rule (unchecked-get module "RawFillRule")}] - (set! wasm/serializers serializers) - (default-fn))) + href (cf/resolve-href "js/render-wasm.wasm")] + (default-fn #js {:locateFile (constantly href)}))) (defonce module (delay (if (exists? js/dynamicImport) - (let [uri (cf/resolve-static-asset "js/render-wasm.js")] + (let [uri (cf/resolve-href "js/render-wasm.js")] (->> (mod/import uri) (p/mcat init-wasm-module) (p/fmap diff --git a/frontend/src/app/render_wasm/api/shared.js b/frontend/src/app/render_wasm/api/shared.js new file mode 100644 index 0000000000..172cb2a97d --- /dev/null +++ b/frontend/src/app/render_wasm/api/shared.js @@ -0,0 +1,242 @@ +export const GrowType = { + "fixed": 0, + "auto-width": 1, + "auto-height": 2, +}; + +export const RawBlendMode = { + "normal": 3, + "screen": 14, + "overlay": 15, + "darken": 16, + "lighten": 17, + "color-dodge": 18, + "color-burn": 19, + "hard-light": 20, + "soft-light": 21, + "difference": 22, + "exclusion": 23, + "multiply": 24, + "hue": 25, + "saturation": 26, + "color": 27, + "luminosity": 28, +}; + +export const RawBlurType = { + "layer-blur": 0, +}; + +export const RawFillData = { + "solid": 0, + "linear": 1, + "radial": 2, + "image": 3, +}; + +export const RawFontStyle = { + "normal": 0, + "italic": 1, +}; + +export const RawAlignItems = { + "start": 0, + "end": 1, + "center": 2, + "stretch": 3, +}; + +export const RawAlignContent = { + "start": 0, + "end": 1, + "center": 2, + "space-between": 3, + "space-around": 4, + "space-evenly": 5, + "stretch": 6, +}; + +export const RawJustifyItems = { + "start": 0, + "end": 1, + "center": 2, + "stretch": 3, +}; + +export const RawJustifyContent = { + "start": 0, + "end": 1, + "center": 2, + "space-between": 3, + "space-around": 4, + "space-evenly": 5, + "stretch": 6, +}; + +export const RawJustifySelf = { + "none": 0, + "auto": 1, + "start": 2, + "end": 3, + "center": 4, + "stretch": 5, +}; + +export const RawAlignSelf = { + "none": 0, + "auto": 1, + "start": 2, + "end": 3, + "center": 4, + "stretch": 5, +}; + +export const RawVerticalAlign = { + "top": 0, + "center": 1, + "bottom": 2, +}; + +export const RawConstraintH = { + "left": 0, + "right": 1, + "leftright": 2, + "center": 3, + "scale": 4, +}; + +export const RawConstraintV = { + "top": 0, + "bottom": 1, + "topbottom": 2, + "center": 3, + "scale": 4, +}; + +export const RawFlexDirection = { + "row": 0, + "row-reverse": 1, + "column": 2, + "column-reverse": 3, +}; + +export const RawWrapType = { + "wrap": 0, + "nowrap": 1, +}; + +export const RawGridDirection = { + "row": 0, + "column": 1, +}; + +export const RawGridTrackType = { + "percent": 0, + "flex": 1, + "auto": 2, + "fixed": 3, +}; + +export const RawSizing = { + "fill": 0, + "fix": 1, + "auto": 2, +}; + +export const RawBoolType = { + "union": 0, + "difference": 1, + "intersection": 2, + "exclusion": 3, +}; + +export const RawSegmentData = { + "move-to": 1, + "line-to": 2, + "curve-to": 3, + "close": 4, +}; + +export const RawShadowStyle = { + "drop-shadow": 0, + "inner-shadow": 1, +}; + +export const RawShapeType = { + "frame": 0, + "group": 1, + "bool": 2, + "rect": 3, + "path": 4, + "text": 5, + "circle": 6, + "svg-raw": 7, +}; + +export const RawStrokeStyle = { + "solid": 0, + "dotted": 1, + "dashed": 2, + "mixed": 3, +}; + +export const RawStrokeCap = { + "none": 0, + "line-arrow": 1, + "triangle-arrow": 2, + "square-marker": 3, + "circle-marker": 4, + "diamond-marker": 5, + "round": 6, + "square": 7, +}; + +export const RawFillRule = { + "nonzero": 0, + "evenodd": 1, +}; + +export const RawStrokeLineCap = { + "butt": 0, + "round": 1, + "square": 2, +}; + +export const RawStrokeLineJoin = { + "miter": 0, + "round": 1, + "bevel": 2, +}; + +export const RawTextAlign = { + "left": 0, + "center": 1, + "right": 2, + "justify": 3, +}; + +export const RawTextDirection = { + "ltr": 0, + "rtl": 1, +}; + +export const RawTextDecoration = { + "none": 0, + "underline": 1, + "line-through": 2, + "overline": 3, +}; + +export const RawTextTransform = { + "none": 0, + "uppercase": 1, + "lowercase": 2, + "capitalize": 3, +}; + +export const RawGrowType = { + "fixed": 0, + "auto-width": 1, + "auto-height": 2, +}; + diff --git a/frontend/src/app/render_wasm/wasm.cljs b/frontend/src/app/render_wasm/wasm.cljs index 7fb59bfebc..3901dc975f 100644 --- a/frontend/src/app/render_wasm/wasm.cljs +++ b/frontend/src/app/render_wasm/wasm.cljs @@ -4,9 +4,43 @@ ;; ;; Copyright (c) KALEIDOS INC -(ns app.render-wasm.wasm) +(ns app.render-wasm.wasm + (:require ["./api/shared.js" :as shared])) (defonce internal-frame-id nil) (defonce internal-module #js {}) -(defonce serializers #js {}) +(defonce serializers + #js {:blur-type shared/RawBlurType + :blend-mode shared/RawBlendMode + :bool-type shared/RawBoolType + :font-style shared/RawFontStyle + :flex-direction shared/RawFlexDirection + :grid-direction shared/RawGridDirection + :grow-type shared/RawGrowType + :align-items shared/RawAlignItems + :align-self shared/RawAlignSelf + :align-content shared/RawAlignContent + :justify-items shared/RawJustifyItems + :justify-content shared/RawJustifyContent + :justify-self shared/RawJustifySelf + :wrap-type shared/RawWrapType + :grid-track-type shared/RawGridTrackType + :shadow-style shared/RawShadowStyle + :stroke-style shared/RawStrokeStyle + :stroke-cap shared/RawStrokeCap + :shape-type shared/RawShapeType + :constraint-h shared/RawConstraintH + :constraint-v shared/RawConstraintV + :sizing shared/RawSizing + :vertical-align shared/RawVerticalAlign + :fill-data shared/RawFillData + :text-align shared/RawTextAlign + :text-direction shared/RawTextDirection + :text-decoration shared/RawTextDecoration + :text-transform shared/RawTextTransform + :segment-data shared/RawSegmentData + :stroke-linecap shared/RawStrokeLineCap + :stroke-linejoin shared/RawStrokeLineJoin + :fill-rule shared/RawFillRule}) + (defonce context-initialized? false) diff --git a/frontend/src/app/util/worker.cljs b/frontend/src/app/util/worker.cljs index fde1796eef..b23bbbee92 100644 --- a/frontend/src/app/util/worker.cljs +++ b/frontend/src/app/util/worker.cljs @@ -89,7 +89,7 @@ (defn init "Return a initialized webworker instance." [path on-error] - (let [instance (js/Worker. path #js {:type "module"}) + (let [instance (js/Worker. path) bus (rx/subject) worker (Worker. instance (rx/to-observable bus)) diff --git a/frontend/src/app/worker/thumbnails.cljs b/frontend/src/app/worker/thumbnails.cljs index ace5c04d80..9f757f037d 100644 --- a/frontend/src/app/worker/thumbnails.cljs +++ b/frontend/src/app/worker/thumbnails.cljs @@ -20,7 +20,6 @@ [app.render-wasm.api :as wasm.api] [app.render-wasm.wasm :as wasm] [app.util.http :as http] - [app.util.modules :as mod] [app.worker.impl :as impl] [beicon.v2.core :as rx] [okulary.core :as l] @@ -29,6 +28,8 @@ (log/set-level! :trace) +(def ^:private ^:const thumbnail-aspect-ratio (/ 2 3)) + (defn- handle-response [{:keys [body status] :as response}] (cond @@ -64,6 +65,10 @@ (rx/map http/conditional-decode-transit) (rx/mapcat handle-response)))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; SVG RENDERING (LEGACY RENDER) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (defn- render-thumbnail [{:keys [page file-id revn] :as params}] (try @@ -98,15 +103,13 @@ (->> (request-data-for-thumbnail file-id revn true) (rx/map render-thumbnail))) -(def init-wasm - (delay - (let [uri (cf/resolve-static-asset "js/render-wasm.js")] - (-> (mod/import (str uri)) - (p/then #(wasm.api/init-wasm-module %)) - (p/then #(set! wasm/internal-module %)))))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; WASM RENDERING +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(mf/defc svg-wrapper - [{:keys [data-uri background width height]}] +(mf/defc svg-wrapper* + {::mf/private true} + [{:keys [uri background width height]}] [:svg {:version "1.1" :xmlns "http://www.w3.org/2000/svg" :xmlnsXlink "http://www.w3.org/1999/xlink" @@ -116,85 +119,97 @@ :background background} :fill "none" :viewBox (dm/str "0 0 " width " " height)} - [:image {:xlinkHref data-uri + [:image {:xlinkHref uri :width width :height height}]]) -(defn blob->uri +(defn- blob->uri [blob] (.readAsDataURL (js/FileReaderSync.) blob)) -(def thumbnail-aspect-ratio (/ 2 3)) +(defn- render-canvas-blob + [canvas width height background] + (->> (.convertToBlob ^js canvas) + (p/fmap (fn [blob] + (rds/renderToStaticMarkup + (mf/element svg-wrapper* + #js {:uri (blob->uri blob) + :width width + :height height + :background background})))))) -(defn render-canvas-blob - [canvas width height background-color] - (-> (.convertToBlob canvas) - (p/then - (fn [blob] - (rds/renderToStaticMarkup - (mf/element - svg-wrapper - #js {:data-uri (blob->uri blob) - :width width - :height height - :background background-color})))))) +(defonce ^:private wasm-module + (delay + (let [module (unchecked-get js/globalThis "WasmModule") + init-fn (unchecked-get module "default") + href (cf/resolve-href "js/render-wasm.wasm")] + (->> (init-fn #js {:locateFile (constantly href)}) + (p/fnly (fn [module cause] + (if cause + (js/console.error cause) + (set! wasm/internal-module module)))))))) -(defn process-wasm-thumbnail +(defn- render-thumbnail-with-wasm [{:keys [id file-id revn width] :as message}] - (->> (rx/from @init-wasm) + (->> (rx/from @wasm-module) (rx/mapcat #(request-data-for-thumbnail file-id revn false)) (rx/mapcat (fn [{:keys [page] :as file}] (rx/create (fn [subs] - (let [background-color (or (:background page) cc/canvas) - height (* width thumbnail-aspect-ratio) - canvas (js/OffscreenCanvas. width height) - init? (wasm.api/init-canvas-context canvas)] + (let [bgcolor (or (:background page) cc/canvas) + height (* width thumbnail-aspect-ratio) + canvas (js/OffscreenCanvas. width height) + init? (wasm.api/init-canvas-context canvas)] (if init? (let [objects (:objects page) - frame (some->> page :thumbnail-frame-id (get objects)) - vbox (if frame - (-> (gsb/get-object-bounds objects frame) - (grc/fix-aspect-ratio thumbnail-aspect-ratio)) - (render/calculate-dimensions objects thumbnail-aspect-ratio)) - zoom (/ width (:width vbox))] + frame (some->> page :thumbnail-frame-id (get objects)) + vbox (if frame + (-> (gsb/get-object-bounds objects frame) + (grc/fix-aspect-ratio thumbnail-aspect-ratio)) + (render/calculate-dimensions objects thumbnail-aspect-ratio)) + zoom (/ width (:width vbox))] (wasm.api/initialize-viewport - objects zoom vbox background-color + objects zoom vbox bgcolor (fn [] (if frame (wasm.api/render-sync-shape (:id frame)) (wasm.api/render-sync)) - (-> (render-canvas-blob canvas width height background-color) - (p/then #(rx/push! subs {:id id :data % :file-id file-id :revn revn})) - (p/catch #(rx/error! subs %)) - (p/finally #(rx/end! subs)))))) - + (->> (render-canvas-blob canvas width height bgcolor) + (p/fnly (fn [data cause] + (if cause + (rx/error! subs cause) + (rx/push! subs + {:id id + :data data + :file-id file-id + :revn revn})) + (rx/end! subs))))))) (rx/end! subs)) - nil))))))) -(defonce thumbs-subject (rx/subject)) +(defonce ^:private + thumbnails-queue + (rx/subject)) -(defonce thumbs-stream - (->> thumbs-subject - (rx/mapcat process-wasm-thumbnail) +(defonce ^:private + thumbnails-stream + (->> thumbnails-queue + (rx/mapcat render-thumbnail-with-wasm) (rx/share))) (defmethod impl/handler :thumbnails/generate-for-file-wasm [message _] (rx/create (fn [subs] - (let [id (uuid/next) - sid - (->> thumbs-stream - (rx/filter #(= id (:id %))) - (rx/subs! - #(do - (rx/push! subs %) - (rx/end! subs))))] - (rx/push! thumbs-subject (assoc message :id id)) - + (let [id (uuid/next) + sid (->> thumbnails-stream + (rx/filter #(= id (:id %))) + (rx/subs! + (fn [result] + (rx/push! subs result) + (rx/end! subs))))] + (rx/push! thumbnails-queue (assoc message :id id)) #(rx/dispose! sid))))) diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 0a61de53b0..6901029051 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -7745,6 +7745,10 @@ msgstr "" "Invalid token value: only none, Uppercase, Lowercase or Capitalize are " "accepted" +#: src/app/main/data/workspace/tokens/errors.cljs:93 +msgid "workspace.tokens.invalid-font-family-token-value" +msgstr "Invalid token value: you can only reference a font-family token" + #: src/app/main/data/workspace/tokens/errors.cljs:85 msgid "workspace.tokens.invalid-text-decoration-token-value" msgstr "Invalid token value: only none, underline and strike-through are accepted" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 6a7d06f23f..8aecd8ba50 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -7670,6 +7670,14 @@ msgstr "" msgid "workspace.tokens.invalid-shadow-type-token-value" msgstr "Tipo de sombra no válida: solo se aceptan 'innerShadow' o 'dropShadow'" +#: src/app/main/data/workspace/tokens/errors.cljs:93 +msgid "workspace.tokens.invalid-font-family-token-value" +msgstr "Valor de token no válido: solo puedes referenciar tokens tipo font-family" + +#: src/app/main/data/workspace/tokens/errors.cljs:85 +msgid "workspace.tokens.invalid-text-decoration-token-value" +msgstr "Valor de token no válido: solo none, underline y strike-through son aceptados" + #: src/app/main/data/workspace/tokens/errors.cljs:93 msgid "workspace.tokens.invalid-token-value-typography" msgstr "Valor no válido: debe hacer referencia a un token tipográfico compuesto." diff --git a/package.json b/package.json index f6a6717e32..22e4bb4a0f 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "license": "MPL-2.0", "author": "Kaleidos INC", "private": true, - "packageManager": "yarn@4.9.1+sha512.f95ce356460e05be48d66401c1ae64ef84d163dd689964962c6888a9810865e39097a5e9de748876c2e0bf89b232d583c33982773e9903ae7a76257270986538", + "packageManager": "yarn@4.12.0+sha512.f45ab632439a67f8bc759bf32ead036a1f413287b9042726b7cc4818b7b49e14e9423ba49b18f9e06ea4941c1ad062385b1d8760a8d5091a1a31e5f6219afca8", "repository": { "type": "git", "url": "https://github.com/penpot/penpot" @@ -15,6 +15,7 @@ "fmt": "./scripts/fmt" }, "devDependencies": { - "@types/node": "^20.12.7" + "@types/node": "^20.12.7", + "esbuild": "^0.25.9" } } diff --git a/render-wasm/_build_env b/render-wasm/_build_env index 902b2c4320..3be4d13957 100644 --- a/render-wasm/_build_env +++ b/render-wasm/_build_env @@ -1,6 +1,6 @@ #!/usr/bin/env bash -CURRENT_VERSION=${CURRENT_VERSION:-develop}; +export CURRENT_VERSION=${CURRENT_VERSION:-develop}; if [ "$NODE_ENV" = "production" ]; then export BUILD_MODE="release"; @@ -8,14 +8,16 @@ else export BUILD_MODE=${1:-debug}; fi -BUILD_NAME="${BUILD_NAME:-render-wasm}" +export BUILD_NAME="${BUILD_NAME:-render-wasm}" +export CARGO_BUILD_TARGET=${CARGO_BUILD_TARGET:-"wasm32-unknown-emscripten"}; +export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.87.0/skia-binaries-e551f334ad5cbdf43abf-wasm32-unknown-emscripten-gl-svg-textlayout-binary-cache-webp.tar.gz"} # 256 MB of initial heap to perform less # initial calls to memory grow. -EM_INITIAL_HEAP=$((256 * 1024 * 1024)) +export EM_INITIAL_HEAP=$((256 * 1024 * 1024)) # 1.0 doubles the heap on every growth. -EM_MEMORY_GROWTH_GEOMETRIC_STEP="0.8" +export EM_MEMORY_GROWTH_GEOMETRIC_STEP="0.8" # Malloc implementation to use. # - dlmalloc: a powerful general-purpose malloc. @@ -25,38 +27,63 @@ EM_MEMORY_GROWTH_GEOMETRIC_STEP="0.8" # - emmalloc-verbose: use emmalloc with assertions + verbose logging. # - emmalloc-memvalidate-verbose: use emmalloc with assertions + heap consistency checking + verbose logging. # Default: dlmalloc -EM_MALLOC="dlmalloc" +export EM_MALLOC="dlmalloc" -EMCC_CFLAGS="--no-entry \ +export EMCC_CFLAGS="--no-entry \ --js-library src/js/wapi.js \ -sASSERTIONS=1 \ -sALLOW_TABLE_GROWTH=1 \ -sALLOW_MEMORY_GROWTH=1 \ -sINITIAL_HEAP=$EM_INITIAL_HEAP \ -sMEMORY_GROWTH_GEOMETRIC_STEP=$EM_MEMORY_GROWTH_GEOMETRIC_STEP \ - -sENVIRONMENT=web \ -sERROR_ON_UNDEFINED_SYMBOLS=0 \ -sMAX_WEBGL_VERSION=2 \ - -sMODULARIZE=1 \ -sEXPORT_NAME=createRustSkiaModule \ -sEXPORTED_RUNTIME_METHODS=GL,stringToUTF8,HEAPU8,HEAP32,HEAPU32,HEAPF32 \ - -sEXPORT_ES6=1" + -sENVIRONMENT=web \ + -sMODULARIZE=1 \ + -sEXPORT_ES6=1"; export EM_CACHE="/tmp/emsdk_cache"; -CARGO_PARAMS="${@:2}"; +export CARGO_PARAMS="${@:2}"; if [ "$BUILD_MODE" = "release" ]; then - CARGO_PARAMS="--release $CARGO_PARAMS" - EMCC_CFLAGS="-Os $EMCC_CFLAGS" + export CARGO_PARAMS="--release $CARGO_PARAMS" + export EMCC_CFLAGS="-Os $EMCC_CFLAGS" else # TODO: Extra parameters that could be good to look into: # -gseparate-dwarf # -gsplit-dwarf # -gsource-map - EMCC_CFLAGS="-g $EMCC_CFLAGS -sVERBOSE=1 -sMALLOC=$EM_MALLOC" + export EMCC_CFLAGS="-g $EMCC_CFLAGS -sVERBOSE=1 -sMALLOC=$EM_MALLOC" fi -export EMCC_CFLAGS; -export CARGO_PARAMS; +function clean { + cargo clean; +} +function build { + cargo build $CARGO_PARAMS; +} + +function copy_artifacts { + DEST=$1; + + cp target/wasm32-unknown-emscripten/$BUILD_MODE/render_wasm.js $DEST/$BUILD_NAME.js; + cp target/wasm32-unknown-emscripten/$BUILD_MODE/render_wasm.wasm $DEST/$BUILD_NAME.wasm; + + sed -i "s/render_wasm.wasm/$BUILD_NAME.wasm?version=$CURRENT_VERSION/g" $DEST/$BUILD_NAME.js; + + npx esbuild target/wasm32-unknown-emscripten/$BUILD_MODE/render_wasm.js \ + --log-level=error \ + --outfile=$DEST/worker/render.js \ + --platform=neutral \ + --format=iife \ + --global-name=WasmModule; +} + +function copy_shared_artifact { + SHARED_FILE=$(find target/wasm32-unknown-emscripten -name render_wasm_shared.js | head -n 1); + cp $SHARED_FILE ../frontend/src/app/render_wasm/api/shared.js; +} diff --git a/render-wasm/build b/render-wasm/build index 2497491260..4683f09e69 100755 --- a/render-wasm/build +++ b/render-wasm/build @@ -7,19 +7,12 @@ pushd $_SCRIPT_DIR; . ./_build_env -set -x +set -x; -export CARGO_BUILD_TARGET=${CARGO_BUILD_TARGET:-"wasm32-unknown-emscripten"}; -export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.87.0/skia-binaries-e551f334ad5cbdf43abf-wasm32-unknown-emscripten-gl-svg-textlayout-binary-cache-webp.tar.gz"} +build; +copy_artifacts "../frontend/resources/public/js"; +copy_shared_artifact; -cargo build $CARGO_PARAMS - -_SHARED_FILE=$(find target/wasm32-unknown-emscripten -name render_wasm_shared.js | head -n 1); - -cat target/wasm32-unknown-emscripten/$BUILD_MODE/render_wasm.js "$_SHARED_FILE" > ../frontend/resources/public/js/$BUILD_NAME.js -cp target/wasm32-unknown-emscripten/$BUILD_MODE/render_wasm.wasm ../frontend/resources/public/js/$BUILD_NAME.wasm -sed -i "s/render_wasm.wasm/$BUILD_NAME.wasm?version=$CURRENT_VERSION/g" ../frontend/resources/public/js/$BUILD_NAME.js; - -exit $? +exit $?; popd diff --git a/render-wasm/src/render/text.rs b/render-wasm/src/render/text.rs index 5c64ed18ab..7285386af5 100644 --- a/render-wasm/src/render/text.rs +++ b/render-wasm/src/render/text.rs @@ -253,11 +253,12 @@ fn draw_text( let layer_rec = SaveLayerRec::default(); canvas.save_layer(&layer_rec); - let mut normalized_line_height = text_content.normalized_line_height(); + let mut previous_line_height = text_content.normalized_line_height(); for paragraph_builder_group in paragraph_builder_groups { - let mut group_offset_y = global_offset_y; + let group_offset_y = global_offset_y; let group_len = paragraph_builder_group.len(); + let mut paragraph_offset_y = previous_line_height; for (paragraph_index, paragraph_builder) in paragraph_builder_group.iter_mut().enumerate() { let mut paragraph = paragraph_builder.build(); @@ -266,12 +267,13 @@ fn draw_text( paragraph.paint(canvas, xy); let line_metrics = paragraph.get_line_metrics(); + if paragraph_index == group_len - 1 { if line_metrics.is_empty() { - group_offset_y += normalized_line_height; + paragraph_offset_y = paragraph.ideographic_baseline(); } else { - normalized_line_height = paragraph.ideographic_baseline(); - group_offset_y += paragraph.ideographic_baseline() * line_metrics.len() as f32; + paragraph_offset_y = paragraph.height(); + previous_line_height = paragraph.ideographic_baseline(); } } @@ -280,7 +282,7 @@ fn draw_text( } } - global_offset_y = group_offset_y; + global_offset_y += paragraph_offset_y; } } diff --git a/render-wasm/test b/render-wasm/test index 2e9eb8d341..85d5547d4a 100755 --- a/render-wasm/test +++ b/render-wasm/test @@ -1,14 +1,14 @@ #!/usr/bin/env bash set -x +export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.87.0/skia-binaries-e551f334ad5cbdf43abf-x86_64-unknown-linux-gnu-gl-svg-textlayout-binary-cache-webp.tar.gz"} +export CARGO_BUILD_TARGET=${CARGO_BUILD_TARGET:-"x86_64-unknown-linux-gnu"}; + _SCRIPT_DIR=$(dirname $0); pushd $_SCRIPT_DIR; . ./_build_env -export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.87.0/skia-binaries-e551f334ad5cbdf43abf-x86_64-unknown-linux-gnu-gl-svg-textlayout-binary-cache-webp.tar.gz"} -export CARGO_BUILD_TARGET=${CARGO_BUILD_TARGET:-"x86_64-unknown-linux-gnu"}; - cargo test --bin render_wasm -- --show-output # Exit with the same status code as cargo test diff --git a/render-wasm/watch b/render-wasm/watch index 7eef370100..59139ec2cb 100755 --- a/render-wasm/watch +++ b/render-wasm/watch @@ -6,21 +6,15 @@ pushd $_SCRIPT_DIR; . ./_build_env set -x - -export CARGO_BUILD_TARGET=${CARGO_BUILD_TARGET:-"wasm32-unknown-emscripten"}; -export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.87.0/skia-binaries-e551f334ad5cbdf43abf-wasm32-unknown-emscripten-gl-svg-textlayout-binary-cache-webp.tar.gz"} - -SHARED_FILE=$(find target/wasm32-unknown-emscripten -name render_wasm_shared.js | head -n 1); - -cat target/wasm32-unknown-emscripten/$BUILD_MODE/render_wasm.js "$SHARED_FILE" > ../frontend/resources/public/js/$BUILD_NAME.js -cp target/wasm32-unknown-emscripten/$BUILD_MODE/render_wasm.wasm ../frontend/resources/public/js/$BUILD_NAME.wasm +build; +copy_artifacts "../frontend/resources/public/js"; +copy_shared_artifact; pushd $_SCRIPT_DIR; + cargo watch \ -x "build $CARGO_PARAMS" \ - -s "cat target/wasm32-unknown-emscripten/$BUILD_MODE/render_wasm.js \"$SHARED_FILE\" > ../frontend/resources/public/js/$BUILD_NAME.js" \ - -s "cp target/wasm32-unknown-emscripten/$BUILD_MODE/render_wasm.wasm ../frontend/resources/public/js/$BUILD_NAME.wasm" \ - -s "sed -i 's/render_wasm.wasm/$BUILD_NAME.wasm?version=$CURRENT_VERSION/g' ../frontend/resources/public/js/$BUILD_NAME.js" \ + -s "./build" \ -s "echo 'DONE\n'"; popd diff --git a/yarn.lock b/yarn.lock index 7c3964d261..19eba5a5a6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,6 +5,188 @@ __metadata: version: 8 cacheKey: 10c0 +"@esbuild/aix-ppc64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/aix-ppc64@npm:0.25.12" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/android-arm64@npm:0.25.12" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/android-arm@npm:0.25.12" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/android-x64@npm:0.25.12" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/darwin-arm64@npm:0.25.12" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/darwin-x64@npm:0.25.12" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/freebsd-arm64@npm:0.25.12" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/freebsd-x64@npm:0.25.12" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-arm64@npm:0.25.12" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-arm@npm:0.25.12" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-ia32@npm:0.25.12" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-loong64@npm:0.25.12" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-mips64el@npm:0.25.12" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-ppc64@npm:0.25.12" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-riscv64@npm:0.25.12" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-s390x@npm:0.25.12" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-x64@npm:0.25.12" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/netbsd-arm64@npm:0.25.12" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/netbsd-x64@npm:0.25.12" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/openbsd-arm64@npm:0.25.12" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/openbsd-x64@npm:0.25.12" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openharmony-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/openharmony-arm64@npm:0.25.12" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/sunos-x64@npm:0.25.12" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/win32-arm64@npm:0.25.12" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/win32-ia32@npm:0.25.12" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/win32-x64@npm:0.25.12" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@types/node@npm:^20.12.7": version: 20.12.12 resolution: "@types/node@npm:20.12.12" @@ -14,11 +196,101 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:^0.25.9": + version: 0.25.12 + resolution: "esbuild@npm:0.25.12" + dependencies: + "@esbuild/aix-ppc64": "npm:0.25.12" + "@esbuild/android-arm": "npm:0.25.12" + "@esbuild/android-arm64": "npm:0.25.12" + "@esbuild/android-x64": "npm:0.25.12" + "@esbuild/darwin-arm64": "npm:0.25.12" + "@esbuild/darwin-x64": "npm:0.25.12" + "@esbuild/freebsd-arm64": "npm:0.25.12" + "@esbuild/freebsd-x64": "npm:0.25.12" + "@esbuild/linux-arm": "npm:0.25.12" + "@esbuild/linux-arm64": "npm:0.25.12" + "@esbuild/linux-ia32": "npm:0.25.12" + "@esbuild/linux-loong64": "npm:0.25.12" + "@esbuild/linux-mips64el": "npm:0.25.12" + "@esbuild/linux-ppc64": "npm:0.25.12" + "@esbuild/linux-riscv64": "npm:0.25.12" + "@esbuild/linux-s390x": "npm:0.25.12" + "@esbuild/linux-x64": "npm:0.25.12" + "@esbuild/netbsd-arm64": "npm:0.25.12" + "@esbuild/netbsd-x64": "npm:0.25.12" + "@esbuild/openbsd-arm64": "npm:0.25.12" + "@esbuild/openbsd-x64": "npm:0.25.12" + "@esbuild/openharmony-arm64": "npm:0.25.12" + "@esbuild/sunos-x64": "npm:0.25.12" + "@esbuild/win32-arm64": "npm:0.25.12" + "@esbuild/win32-ia32": "npm:0.25.12" + "@esbuild/win32-x64": "npm:0.25.12" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-arm64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/openharmony-arm64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/c205357531423220a9de8e1e6c6514242bc9b1666e762cd67ccdf8fdfdc3f1d0bd76f8d9383958b97ad4c953efdb7b6e8c1f9ca5951cd2b7c5235e8755b34a6b + languageName: node + linkType: hard + "penpot@workspace:.": version: 0.0.0-use.local resolution: "penpot@workspace:." dependencies: "@types/node": "npm:^20.12.7" + esbuild: "npm:^0.25.9" languageName: unknown linkType: soft