diff --git a/.serena/memories/workflow/creating-prs.md b/.serena/memories/workflow/creating-prs.md index 204fd49c76..c0f763f9e3 100644 --- a/.serena/memories/workflow/creating-prs.md +++ b/.serena/memories/workflow/creating-prs.md @@ -38,7 +38,7 @@ Include concise sections covering: PR descriptions follow this structure: ```markdown -**Note:** This PR was created with AI assistance as part of the Penpot MCP self-improvement initiative. +**Note:** This PR was created with AI assistance. ## What diff --git a/docker/images/docker-compose.yaml b/docker/images/docker-compose.yaml index 94193a977c..59b326c76d 100644 --- a/docker/images/docker-compose.yaml +++ b/docker/images/docker-compose.yaml @@ -197,10 +197,10 @@ services: - penpot environment: - << : [*penpot-secret-key] + << : [*penpot-secret-key, *penpot-public-uri] # Don't touch it; this uses an internal docker network to # communicate with the frontend. - PENPOT_PUBLIC_URI: http://penpot-frontend:8080 + PENPOT_INTERNAL_URI: http://penpot-frontend:8080 ## Valkey (or previously Redis) is used for the websockets notifications. PENPOT_REDIS_URI: redis://penpot-valkey/0 diff --git a/docs/technical-guide/configuration.md b/docs/technical-guide/configuration.md index eec62f63e4..49d5a23e8d 100644 --- a/docs/technical-guide/configuration.md +++ b/docs/technical-guide/configuration.md @@ -651,6 +651,21 @@ PENPOT_EXPORTER_URI: http://your-penpot-exporter:6061 These variables are used for generate correct nginx.conf file on container startup. +### Exporter + +The exporter uses this variable: + +```bash +# Exporter +PENPOT_INTERNAL_URI: http://penpot-frontend:8080 +``` + +- `PENPOT_INTERNAL_URI`: The URI used by the exporter's headless browser to + communicate with the frontend (internal Docker network). Defaults to + `PENPOT_PUBLIC_URI` if not set. The default value + `http://penpot-frontend:8080` used in the docker-compose is a good default and + it is recommended to keep it unchanged. + ## Other flags There are other flags that are useful for a more customized Penpot experience. This section has the list of the flags meant diff --git a/exporter/src/app/config.cljs b/exporter/src/app/config.cljs index ff32391f82..8010032680 100644 --- a/exporter/src/app/config.cljs +++ b/exporter/src/app/config.cljs @@ -22,6 +22,7 @@ (def ^:private defaults {:public-uri "http://localhost:3449" + :internal-uri nil :tenant "default" :host "localhost" :http-server-port 6061 @@ -33,6 +34,7 @@ [:map {:title "config"} [:secret-key :string] [:public-uri {:optional true} ::sm/uri] + [:internal-uri {:optional true} ::sm/uri] [:exporter-shared-key {:optional true} :string] [:host {:optional true} :string] [:tenant {:optional true} :string] @@ -100,6 +102,12 @@ ([key default] (c/get config key default))) +(defn get-internal-uri + "Returns internal-uri if set, otherwise falls back to public-uri." + [] + (or (c/get config :internal-uri) + (c/get config :public-uri))) + (def management-key (let [key (or (c/get config :exporter-shared-key) (let [secret-key (c/get config :secret-key) diff --git a/exporter/src/app/core.cljs b/exporter/src/app/core.cljs index 91d4ee702e..5ce68ebf98 100644 --- a/exporter/src/app/core.cljs +++ b/exporter/src/app/core.cljs @@ -21,6 +21,7 @@ [& _] (l/info :msg "initializing" :public-uri (str (cf/get :public-uri)) + :internal-uri (str (cf/get-internal-uri)) :version (:full cf/version)) (p/do! (bwr/init) diff --git a/exporter/src/app/handlers/resources.cljs b/exporter/src/app/handlers/resources.cljs index 9b2f37d6cc..4c4bb7225c 100644 --- a/exporter/src/app/handlers/resources.cljs +++ b/exporter/src/app/handlers/resources.cljs @@ -79,7 +79,7 @@ :method "POST" :body fdata :dispatcher agent} - uri (-> (cf/get :public-uri) + uri (-> (cf/get-internal-uri) (u/ensure-path-slash) (u/join "api/management/methods/upload-tempfile") (str))] diff --git a/exporter/src/app/renderer/bitmap.cljs b/exporter/src/app/renderer/bitmap.cljs index 3579a4839e..c2720eb025 100644 --- a/exporter/src/app/renderer/bitmap.cljs +++ b/exporter/src/app/renderer/bitmap.cljs @@ -62,7 +62,7 @@ :skip-children skip-children :wasm (when is-wasm "true") :scale scale} - uri (-> (cf/get :public-uri) + uri (-> (cf/get-internal-uri) (u/ensure-path-slash) (u/join "render.html") (assoc :query (u/map->query-string params)))] diff --git a/exporter/src/app/renderer/pdf.cljs b/exporter/src/app/renderer/pdf.cljs index b953730be4..ba4118c1e8 100644 --- a/exporter/src/app/renderer/pdf.cljs +++ b/exporter/src/app/renderer/pdf.cljs @@ -77,7 +77,7 @@ (on-object (assoc object :path path)) (p/recur (rest objects))))))] - (let [base-uri (-> (cf/get :public-uri) + (let [base-uri (-> (cf/get-internal-uri) (u/ensure-path-slash))] (bw/exec! (prepare-options base-uri) (partial render base-uri))))) diff --git a/exporter/src/app/renderer/svg.cljs b/exporter/src/app/renderer/svg.cljs index 7a47e46fe0..c9fee2f764 100644 --- a/exporter/src/app/renderer/svg.cljs +++ b/exporter/src/app/renderer/svg.cljs @@ -108,6 +108,18 @@ {:width width :height height})) +(defn- replace-internal-uris + "Replaces internal-uri references with public-uri in SVG output. + This ensures that font URLs and other resource references in the + exported SVG use the public-facing URI accessible to end users." + [svg-content] + (let [internal-uri (str (cf/get-internal-uri)) + public-uri (str (cf/get :public-uri))] + (if (and (not= internal-uri public-uri) + (str/includes? svg-content internal-uri)) + (str/replace svg-content internal-uri public-uri) + svg-content))) + (defn render [{:keys [page-id file-id share-id objects token scale type]} on-object] (letfn [(convert-to-ppm [pngpath] @@ -320,7 +332,9 @@ result (if (contains? cf/flags :exporter-svgo) (svgo/optimize result svgo/defaultOptions) - result)] + result) + + result (replace-internal-uris result)] ;; (println "------- ORIGIN:") ;; (cljs.pprint/pprint (xml->clj xmldata)) @@ -349,7 +363,7 @@ :render-embed true :object-id (mapv :id objects) :route "objects"} - uri (-> (cf/get :public-uri) + uri (-> (cf/get-internal-uri) (u/ensure-path-slash) (u/join "render.html") (assoc :query (u/map->query-string params)))] diff --git a/frontend/src/app/main/ui/components/select.scss b/frontend/src/app/main/ui/components/select.scss index 6baab75ac1..438e0873fa 100644 --- a/frontend/src/app/main/ui/components/select.scss +++ b/frontend/src/app/main/ui/components/select.scss @@ -150,7 +150,7 @@ .separator { margin: 0; block-size: $sz-12; - border-block-start: $b-1 solid var(--color-background-primary); + border-block-start: $b-1 solid var(--color-background-quaternary); } &[data-direction="up"] { diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs index b39d62c1cb..8e21f18232 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs @@ -171,12 +171,20 @@ on-caps-start-change (mf/use-fn (mf/deps index on-stroke-cap-start-change) - #(on-stroke-cap-start-change index (keyword %))) + (fn [cap] + (let [cap (if (= cap "none") + nil + (keyword cap))] + (on-stroke-cap-start-change index cap)))) on-caps-end-change (mf/use-fn (mf/deps index on-stroke-cap-end-change) - #(on-stroke-cap-end-change index (keyword %))) + (fn [cap] + (let [cap (if (= cap "none") + nil + (keyword cap))] + (on-stroke-cap-end-change index cap)))) on-detach-token-color (mf/use-fn @@ -191,16 +199,16 @@ (on-detach-token token #{:stroke-width}))) stroke-caps-options - [{:value nil :label (tr "workspace.options.stroke-cap.none")} - :separator - {:value :line-arrow :label (tr "workspace.options.stroke-cap.line-arrow-short") :icon :stroke-arrow} - {:value :triangle-arrow :label (tr "workspace.options.stroke-cap.triangle-arrow-short") :icon :stroke-triangle} - {:value :square-marker :label (tr "workspace.options.stroke-cap.square-marker-short") :icon :stroke-rectangle} - {:value :circle-marker :label (tr "workspace.options.stroke-cap.circle-marker-short") :icon :stroke-circle} - {:value :diamond-marker :label (tr "workspace.options.stroke-cap.diamond-marker-short") :icon :stroke-diamond} - :separator - {:value :round :label (tr "workspace.options.stroke-cap.round") :icon :stroke-rounded} - {:value :square :label (tr "workspace.options.stroke-cap.square") :icon :stroke-squared}] + [{:id "none" :value "none" :label (tr "workspace.options.stroke-cap.none")} + {:label "" :type :separator :id "separator"} + {:id "line-arrow" :value :line-arrow :label (tr "workspace.options.stroke-cap.line-arrow-short") :icon i/stroke-arrow} + {:id "triangle-arrow" :value :triangle-arrow :label (tr "workspace.options.stroke-cap.triangle-arrow-short") :icon i/stroke-triangle} + {:id "square-marker" :value :square-marker :label (tr "workspace.options.stroke-cap.square-marker-short") :icon i/stroke-rectangle} + {:id "circle-marker" :value :circle-marker :label (tr "workspace.options.stroke-cap.circle-marker-short") :icon i/stroke-circle} + {:id "diamond-marker" :value :diamond-marker :label (tr "workspace.options.stroke-cap.diamond-marker-short") :icon i/stroke-diamond} + {:label "" :type :separator :id "separator"} + {:id "round" :value :round :label (tr "workspace.options.stroke-cap.round") :icon i/stroke-rounded} + {:id "square" :value :square :label (tr "workspace.options.stroke-cap.square") :icon i/stroke-squared}] on-cap-switch (mf/use-fn @@ -340,16 +348,18 @@ ;; Stroke Caps (when show-caps [:div {:class (stl/css :stroke-caps-options)} - [:& select {:default-value (:stroke-cap-start stroke) - :options stroke-caps-options - :disabled hidden? - :on-change on-caps-start-change}] + [:> select* {:default-selected (or (d/name (:stroke-cap-start stroke)) "none") + :options stroke-caps-options + :data-testid "stroke.cap-start" + :disabled hidden? + :on-change on-caps-start-change}] [:> icon-button* {:variant "secondary" :aria-label (tr "labels.switch") :disabled hidden? :on-click on-cap-switch :icon i/switch}] - [:& select {:default-value (:stroke-cap-end stroke) - :options stroke-caps-options - :disabled hidden? - :on-change on-caps-end-change}]])])) + [:> select* {:default-selected (or (d/name (:stroke-cap-end stroke)) "none") + :options stroke-caps-options + :data-testid "stroke.cap-end" + :disabled hidden? + :on-change on-caps-end-change}]])]))