mirror of
https://github.com/penpot/penpot.git
synced 2026-09-10 05:58:47 +00:00
🐛 Fix missing text in legacy SVG board thumbnails (#11552)
* 🐛 Fix missing text in legacy SVG board thumbnails Board thumbnails rendered by frame-imposter used React's renderToStaticMarkup, a synchronous pass with no live DOM to measure text against. Text shapes without a persisted position-data value therefore rendered as nothing, so a cached board thumbnail silently lost its text until the board was hovered, selected, or the canvas was zoomed past 130%, all of which bypass the cached thumbnail in favor of live content. frame-imposter now provides the same is-render? context the standalone exporter already sets, so text without position-data falls back to the synchronous foreignObject renderer instead of rendering nothing. Thumbnails cached before this fix stay broken until something regenerates them, so on each page load, board thumbnails containing text are opportunistically regenerated once per browser (tracked via local-storage) so existing files self-heal without requiring an edit. AI-assisted-by: claude-sonnet-5 * ♻️ Use a transducer in heal-stale-text-thumbnails Fixes a PR review comment: the frame filtering/mapping was spread across four separate rx operators. Collapse it into a single transducer pass over frame-ids, leaving only one rx/map to perform the mark-healed side effect and build the update-thumbnail action.
This commit is contained in:
parent
e96a75d366
commit
011feeaf71
@ -12,6 +12,7 @@
|
||||
[app.common.thumbnails :as thc]
|
||||
[app.common.time :as ct]
|
||||
[app.common.types.component :as ctc]
|
||||
[app.common.types.shape-tree :as ctt]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.helpers :as dsh]
|
||||
@ -23,6 +24,7 @@
|
||||
[app.main.render :as render]
|
||||
[app.main.repo :as rp]
|
||||
[app.util.queue :as q]
|
||||
[app.util.storage :as storage]
|
||||
[app.util.timers :as tm]
|
||||
[app.util.webapi :as wapi]
|
||||
[beicon.v2.core :as rx]
|
||||
@ -289,13 +291,60 @@
|
||||
(mapcat get-frame-ids-cached))
|
||||
changes))))
|
||||
|
||||
;; Board thumbnails used to render text shapes without position-data as
|
||||
;; nothing instead of falling back to the foreignObject renderer (see
|
||||
;; frame-imposter in app.main.render), so any board thumbnail cached before
|
||||
;; that fix may be missing its text. The backend doesn't tell the client
|
||||
;; when a fetched thumbnail was generated, so we can't tell stale apart from
|
||||
;; fresh by inspecting it; instead each board thumbnail with text content is
|
||||
;; regenerated at most once per browser, tracked via local-storage so repeat
|
||||
;; visits (once healed) don't keep re-rendering it.
|
||||
(def ^:private healed-storage-key ::healed-text-thumbnails)
|
||||
|
||||
(defn- frame-has-text?
|
||||
[objects frame-id]
|
||||
(->> (cfh/get-children-with-self objects frame-id)
|
||||
(some cfh/text-shape?)
|
||||
(some?)))
|
||||
|
||||
(defn- unhealed-text-thumbnail?
|
||||
[state object-id]
|
||||
(and (some? (dm/get-in state [:thumbnails object-id :uri]))
|
||||
(not (contains? (get @storage/global healed-storage-key) object-id))))
|
||||
|
||||
(defn- mark-thumbnail-healed!
|
||||
[object-id]
|
||||
(swap! storage/global update healed-storage-key (fnil conj #{}) object-id))
|
||||
|
||||
(defn- heal-stale-text-thumbnails
|
||||
"Emits an `update-thumbnail` for every board on the page that has text
|
||||
content and hasn't already been healed (see `healed-storage-key`) in this
|
||||
browser."
|
||||
[state file-id page-id]
|
||||
(let [objects (-> (dsh/lookup-file-data state file-id)
|
||||
(dsh/get-page page-id)
|
||||
:objects)
|
||||
frame-ids (ctt/get-root-frames-ids objects)
|
||||
xf (comp
|
||||
(filter #(frame-has-text? objects %))
|
||||
(keep
|
||||
(fn [frame-id]
|
||||
(let [object-id (thc/fmt-object-id file-id page-id frame-id "frame")]
|
||||
(when (unhealed-text-thumbnail? state object-id)
|
||||
[frame-id object-id])))))]
|
||||
(->> (rx/from (eduction xf frame-ids))
|
||||
(rx/map
|
||||
(fn [[frame-id object-id]]
|
||||
(mark-thumbnail-healed! object-id)
|
||||
(update-thumbnail file-id page-id frame-id "frame" "heal-stale-text-thumbnails"))))))
|
||||
|
||||
(defn watch-state-changes
|
||||
"Watch the state for changes inside frames. If a change is detected will force a rendering
|
||||
of the frame data so the thumbnail can be updated."
|
||||
[file-id page-id]
|
||||
(ptk/reify ::watch-state-changes
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ stream]
|
||||
(watch [_ state stream]
|
||||
(let [stopper-s (rx/filter
|
||||
(fn [event]
|
||||
(as-> (ptk/type event) type
|
||||
@ -330,6 +379,10 @@
|
||||
(rx/tap #(l/trc :hint "buffer initialized")))]
|
||||
|
||||
(->> (rx/merge
|
||||
;; Heal boards with text whose cached thumbnail may predate the
|
||||
;; text-position fix (see heal-stale-text-thumbnails).
|
||||
(heal-stale-text-thumbnails state file-id page-id)
|
||||
|
||||
;; Perform instant thumbnail cleaning of affected frames
|
||||
;; and interrupt any ongoing update-thumbnail process
|
||||
;; related to current frame-id
|
||||
|
||||
@ -266,16 +266,17 @@
|
||||
[{:keys [objects frame vbox x y width height background]}]
|
||||
(let [shape-wrapper (shape-wrapper-factory objects)]
|
||||
[:& (mf/provider muc/render-thumbnails) {:value false}
|
||||
[:svg {:view-box vbox
|
||||
:width (ust/format-precision width viewbox-decimal-precision)
|
||||
:height (ust/format-precision height viewbox-decimal-precision)
|
||||
:version "1.1"
|
||||
:xmlns "http://www.w3.org/2000/svg"
|
||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||
:fill "none"}
|
||||
(when (some? background)
|
||||
[:rect {:x x :y y :width width :height height :fill background}])
|
||||
[:& shape-wrapper {:shape frame}]]]))
|
||||
[:& (mf/provider muc/is-render?) {:value true}
|
||||
[:svg {:view-box vbox
|
||||
:width (ust/format-precision width viewbox-decimal-precision)
|
||||
:height (ust/format-precision height viewbox-decimal-precision)
|
||||
:version "1.1"
|
||||
:xmlns "http://www.w3.org/2000/svg"
|
||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||
:fill "none"}
|
||||
(when (some? background)
|
||||
[:rect {:x x :y y :width width :height height :fill background}])
|
||||
[:& shape-wrapper {:shape frame}]]]]))
|
||||
|
||||
;; Component that serves for render frame thumbnails, mainly used in
|
||||
;; the viewer and inspector
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user