Compare commits

..

11 Commits
2.5.3 ... 2.5.4

Author SHA1 Message Date
Alejandro Alonso
8affefbbab 📎 Update changelog 2025-03-18 10:13:12 +01:00
jdo-odoo
0225919a45 📚 Fix typos in shortcut and insert image section
Signed-off-by: jdo-odoo <108932862+jdo-odoo@users.noreply.github.com>
2025-03-18 10:09:58 +01:00
luisδμ
5155cf2b23 🐛 Fix clicking on a comment at the viewer's sidebar is not opening threads (#6083) 2025-03-17 12:25:23 +01:00
Andrey Antukh
7403f60366 Merge pull request #6076 from penpot/alotor-fix-problem-inspect
🐛 Fix problem with readonly and inspect
2025-03-14 15:50:01 +01:00
Alejandro
a8c34ccc1a Merge pull request #6070 from penpot/alotor-bugfix-grid-layout
🐛 Fix problem with grid component propagation
2025-03-14 12:39:54 +01:00
alonso.torres
8c501db2fa 🐛 Fix problem with readonly and inspect 2025-03-14 12:24:34 +01:00
alonso.torres
d2fbb9dfa7 🐛 Fix problem with grid component propagation 2025-03-14 11:50:55 +01:00
Andrey Antukh
05d6d2fcd4 🐛 Fix several corner cases that causes race conditions on workspace and dashboard loading
* 🐛 Fix several race conditions on workspace and dashboard code

It also fixes a corner case that happens when penpot workspace
is loaded in a background tab on firefox.

* 🐛 Add missing team-id prop to several file returning endpoints
2025-03-14 09:55:41 +01:00
Andrey Antukh
61800d8945 Merge pull request #6074 from penpot/dfelinto-export-webp
🎉 Add support for WEBP format for shape export
2025-03-13 16:24:55 +01:00
Dalai Felinto
f450c9dbe3 🎉 Add support for WEBP format on shape export
It is very convenient to be able to export WEBP right from penpot.
Otherwise users have to first download to PNG then convert it locally.

---

Playwright only supports JPEG and PNG. So in order to support WEBP I had
to first generate a PNG and then convert it afterwards.

Signed-off-by: Dalai Felinto <dalai@blender.org>
2025-03-13 16:15:30 +01:00
Andrey Antukh
e3b3fa3342 📎 Update changelog 2025-03-13 09:32:19 +01:00
33 changed files with 222 additions and 196 deletions

View File

@@ -1,28 +1,30 @@
# CHANGELOG
## 2.5.3
### :rocket: Epics and highlights
### :boom: Breaking changes & Deprecations
### :heart: Community contributions (Thank you!)
## 2.5.4
### :sparkles: New features
- Add support for WEBP format on shape export [Github #6053](https://github.com/penpot/penpot/pull/6053) and [Github #6074](https://github.com/penpot/penpot/pull/6074)
### :bug: Bugs fixed
- Fix feature loading on workspace when opening a file in a background
tab [Taiga #10377](https://tree.taiga.io/project/penpot/issue/10377)
- Fix minor inconsistencies on RPC `get-file-libraries` and `get-file`
methods (add missing team-id prop)
- Fix problem with viewer role and inspect mode [Taiga #9751](https://tree.taiga.io/project/penpot/issue/9751)
- Fix error when clicking on a comment at the viewer's sidebar [Taiga #10465](https://tree.taiga.io/project/penpot/issue/10465)
## 2.5.3
### :bug: Bugs fixed
- Component sync issues with multiple tabs [Taiga #10471](https://tree.taiga.io/project/penpot/issue/10471)
## 2.5.2
### :rocket: Epics and highlights
### :boom: Breaking changes & Deprecations
### :heart: Community contributions (Thank you!)
### :sparkles: New features
- When the workspace is empty, set default the board creation tool [Taiga #9425](https://tree.taiga.io/project/penpot/us/9425)
### :bug: Bugs fixed
@@ -34,22 +36,12 @@
## 2.5.1
### :rocket: Epics and highlights
### :boom: Breaking changes & Deprecations
### :heart: Community contributions (Thank you!)
### :sparkles: New features
- Improve Nginx entryponit to get the resolvers dinamically by default
### :bug: Bugs fixed
## 2.5.0
### :rocket: Epics and highlights
### :boom: Breaking changes & Deprecations
Although this is not a breaking change, we believe its important to highlight it in this
@@ -78,9 +70,6 @@ If you have a big database and many cores available, you can reduce the time of
all files by increasing paralelizacion changing the `max-jobs` value from 1 to N (where N
is a number of cores)
### :heart: Community contributions (Thank you!)
### :sparkles: New features
- [GRADIENTS] New gradients UI with multi-stop support. [Taiga #3418](https://tree.taiga.io/project/penpot/epic/3418)

View File

@@ -323,6 +323,7 @@
file (-> (get-file cfg id :project-id project-id)
(assoc :permissions perms)
(assoc :team-id (:id team))
(check-version!))]
(-> (cfeat/get-team-enabled-features cf/flags team)
@@ -613,6 +614,7 @@
SELECT l.id,
l.features,
l.project_id,
p.team_id,
l.created_at,
l.modified_at,
l.deleted_at,
@@ -622,6 +624,7 @@
l.synced_at,
l.is_shared
FROM libs AS l
INNER JOIN project AS p ON (p.id = l.project_id)
WHERE l.deleted_at IS NULL OR l.deleted_at > now();")
(defn get-file-libraries

View File

@@ -1722,13 +1722,26 @@
(pcb/update-shapes
[shape-copy-id]
(fn [shape-copy objects]
(let [ids-map
(let [component-page
(ctf/get-component-page main-container main-component)
component-swap-children
(->> shape-main
:shapes
(map #(get (:objects component-page) %))
(filter #(some? (ctk/get-swap-slot %)))
(group-by ctk/get-swap-slot))
ids-map
(into {}
(comp
(map #(get objects %))
(keep
(fn [copy-shape]
(let [main-shape (ctf/get-ref-shape main-container main-component copy-shape)]
(let [main-shape
(if (some? (ctk/get-swap-slot copy-shape))
(first (get component-swap-children (ctk/get-swap-slot copy-shape)))
(ctf/get-ref-shape main-container main-component copy-shape))]
[(:id main-shape) (:id copy-shape)]))))
(:shapes shape-copy))
@@ -1744,7 +1757,8 @@
main-cells (-> shape-main (ctl/remap-grid-cells ids-map) :layout-grid-cells)]
(-> shape-copy
(assoc :layout-grid-cells
(ctl/merge-cells copy-cells main-cells omit-touched?)))))
(ctl/merge-cells main-cells copy-cells omit-touched?))
(ctl/assign-cells objects))))
{:ignore-touched true :with-objects? true})))
(defn- update-grid-main-attrs

View File

@@ -8,7 +8,7 @@
(:require
[app.common.schema :as sm]))
(def types #{:png :jpeg :svg :pdf})
(def types #{:png :jpeg :webp :svg :pdf})
(def schema:export
[:map {:title "ShapeExport"}

View File

@@ -1643,15 +1643,10 @@
untouched as possible"
[target-cells source-cells omit-touched?]
(if omit-touched?
(letfn [(get-data [cells id]
(dissoc (get cells id) :row :column :row-span :column-span))
(merge-cells [source-cell target-cell]
(letfn [(merge-cells [source-cell target-cell]
(-> source-cell
(d/patch-object
(dissoc target-cell :shapes :row :column :row-span :column-span))
(cond-> (d/not-empty? (:shapes target-cell))
(assoc :shapes (:shapes target-cell)))))]
(dissoc target-cell :row :column :row-span :column-span))))]
(let [deleted-cells
(into #{}
(filter #(not (contains? source-cells %)))
@@ -1659,10 +1654,7 @@
touched-cells
(into #{}
(filter #(and
(not (contains? deleted-cells %))
(not= (get-data source-cells %)
(get-data target-cells %))))
(filter #(not (contains? deleted-cells %)))
(keys target-cells))]
(->> touched-cells

View File

@@ -27,7 +27,7 @@ title: 07· Exporting objects
<ul>
<li><strong>Size</strong> - Options for the most common sizing scales.</li>
<li><strong>Suffix</strong> - Especially useful if you are exporting at different scales.</li>
<li><strong>File format</strong> - PNG, SVG, JPEG, PDF.</li>
<li><strong>File format</strong> - PNG, JPEG, WEBP, SVG, PDF.</li>
</ul>
<h2 id="export-multiple-elements">Exporting multiple elements</h2>

View File

@@ -166,7 +166,7 @@ a design.</p>
<h2 id="curves">Curves (freehand)</h2>
<p>The curve tool allows a path to be created directly in a freehand mode.
Select the curve tool by clicking on the icon at the toolbar or pressing <kbd>Ctrl/⌘</kbd> + <kbd>c</kbd>.
Select the curve tool by clicking on the icon at the toolbar or pressing <kbd>Shift/⇧</kbd> + <kbd>c</kbd>.
<p>The path created will contain a lot of points, but it is edited the same way as any other curve.</p>
<h2 id="paths">Paths (bezier)</h2>
@@ -206,7 +206,7 @@ You can choose to edit individual nodes or create new ones. Press <kbd>Esc</kbd>
<h3>Insert images</h3>
<p>There are several options for inserting an image into a Penpot file:</p>
<ul>
<li>Use the <strong>image tool</strong> at the toolbar or press <kbd>K</kbd> to inspect images in your file system.</li>
<li>Use the <strong>image tool</strong> at the toolbar or press <kbd>K</kbd> to insert images in your file system.</li>
<li><strong>Drag</strong> an image from your computer to the viewport.</li>
<li>Copy an image & paste it or drag it right from a <strong>browser</strong>.</li>
<li>Drag an image from a Penpot <strong>library</strong>.</li>

View File

@@ -15,7 +15,7 @@
(s/def ::name ::us/string)
(s/def ::suffix ::us/string)
(s/def ::type #{:jpeg :png :pdf :svg})
(s/def ::type #{:png :jpeg :webp :pdf :svg})
(s/def ::page-id ::us/uuid)
(s/def ::file-id ::us/uuid)
(s/def ::share-id ::us/uuid)
@@ -40,6 +40,7 @@
(case type
:png (rb/render params on-object)
:jpeg (rb/render params on-object)
:webp (rb/render params on-object)
:pdf (rp/render params on-object)
:svg (rs/render params on-object)))

View File

@@ -34,7 +34,11 @@
(bw/wait-for node)
(case type
:png (bw/screenshot node {:omit-background? true :type type :path path})
:jpeg (bw/screenshot node {:omit-background? false :type type :path path}))
:jpeg (bw/screenshot node {:omit-background? false :type type :path path})
:webp (p/let [png-path (sh/tempfile :prefix "penpot.tmp.render.bitmap." :suffix ".png")]
;; playwright only supports jpg and png, we need to convert it afterwards
(bw/screenshot node {:omit-background? true :type :png :path png-path})
(sh/run-cmd! (str "convert " png-path " -quality 100 WEBP:" path))))
(on-object (assoc object :path path))))
(render [uri page]

View File

@@ -15,6 +15,7 @@
(case type
:png ".png"
:jpeg ".jpg"
:webp ".webp"
:svg ".svg"
:pdf ".pdf"
:zip ".zip"))
@@ -26,6 +27,7 @@
:pdf "application/pdf"
:svg "image/svg+xml"
:jpeg "image/jpeg"
:png "image/png"))
:png "image/png"
:webp "image/webp"))

View File

@@ -38,7 +38,9 @@
(declare process-message)
(defn initialize
[]
[team-id]
(assert (uuid? team-id) "expected uuid instance for `team-id`")
(ptk/reify ::initialize
ptk/WatchEvent
(watch [_ state stream]
@@ -46,8 +48,8 @@
profile-id (:profile-id state)]
(->> (rx/merge
(rx/of (fetch-projects)
(df/fetch-fonts))
(rx/of (fetch-projects team-id)
(df/fetch-fonts team-id))
(->> stream
(rx/filter (ptk/type? ::dws/message))
(rx/map deref)
@@ -60,8 +62,8 @@
(rx/take-until stopper))))))
(defn finalize
[]
(ptk/data-event ::finalize {}))
[team-id]
(ptk/data-event ::finalize {:team-id team-id}))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Data Fetching (context aware: current team)
@@ -69,7 +71,7 @@
;; --- EVENT: fetch-projects
(defn projects-fetched
(defn- projects-fetched
[projects]
(ptk/reify ::projects-fetched
ptk/UpdateEvent
@@ -80,13 +82,12 @@
projects))))
(defn fetch-projects
[]
[team-id]
(ptk/reify ::fetch-projects
ptk/WatchEvent
(watch [_ state _]
(let [team-id (:current-team-id state)]
(->> (rp/cmd! :get-projects {:team-id team-id})
(rx/map projects-fetched))))))
(watch [_ _ _]
(->> (rp/cmd! :get-projects {:team-id team-id})
(rx/map projects-fetched)))))
;; --- EVENT: search
@@ -115,7 +116,7 @@
;; --- EVENT: recent-files
(defn recent-files-fetched
(defn- recent-files-fetched
[files]
(ptk/reify ::recent-files-fetched
ptk/UpdateEvent
@@ -126,13 +127,14 @@
(update :files d/merge files))))))
(defn fetch-recent-files
[]
(ptk/reify ::fetch-recent-files
ptk/WatchEvent
(watch [_ state _]
(let [team-id (:current-team-id state)]
(->> (rp/cmd! :get-team-recent-files {:team-id team-id})
(rx/map recent-files-fetched))))))
([] (fetch-recent-files nil))
([team-id]
(ptk/reify ::fetch-recent-files
ptk/WatchEvent
(watch [_ state _]
(when-let [team-id (or team-id (:current-team-id state))]
(->> (rp/cmd! :get-team-recent-files {:team-id team-id})
(rx/map recent-files-fetched)))))))
;; --- EVENT: fetch-template-files

View File

@@ -266,10 +266,10 @@
(defn export-shapes-event
[exports origin]
(let [types (reduce (fn [counts {:keys [type]}]
(if (#{:png :pdf :svg :jpeg} type)
(if (#{:png :jpeg :webp :svg :pdf} type)
(update counts type inc)
counts))
{:png 0, :pdf 0, :svg 0, :jpeg 0}
{:png 0, :jpeg 0, :webp 0, :pdf 0, :svg 0}
exports)]
(ptk/event
::ev/event (merge types

View File

@@ -73,13 +73,12 @@
(fonts/register! :custom fonts))))))
(defn fetch-fonts
[]
(ptk/reify ::load-team-fonts
[team-id]
(ptk/reify ::fetch-fonts
ptk/WatchEvent
(watch [_ state _]
(let [team-id (:current-team-id state)]
(->> (rp/cmd! :get-font-variants {:team-id team-id})
(rx/map fonts-fetched))))))
(watch [_ _ _]
(->> (rp/cmd! :get-font-variants {:team-id team-id})
(rx/map fonts-fetched)))))
(defn process-upload
"Given a seq of blobs and the team id, creates a ready-to-use fonts

View File

@@ -64,13 +64,14 @@
(update :profiles merge (d/index-by :id members))))))
(defn fetch-members
[]
(ptk/reify ::fetch-members
ptk/WatchEvent
(watch [_ state _]
(let [team-id (:current-team-id state)]
(->> (rp/cmd! :get-team-members {:team-id team-id})
(rx/map (partial members-fetched team-id)))))))
([] (fetch-members nil))
([team-id]
(ptk/reify ::fetch-members
ptk/WatchEvent
(watch [_ state _]
(when-let [team-id (or team-id (:current-team-id state))]
(->> (rp/cmd! :get-team-members {:team-id team-id})
(rx/map (partial members-fetched team-id))))))))
(defn- invitations-fetched
[team-id invitations]
@@ -88,41 +89,20 @@
(->> (rp/cmd! :get-team-invitations {:team-id team-id})
(rx/map (partial invitations-fetched team-id)))))))
(defn set-current-team
[{:keys [id permissions features] :as team}]
(ptk/reify ::set-current-team
ptk/UpdateEvent
(update [_ state]
(-> state
;; FIXME: redundant operation, only necessary on workspace
;; until workspace initialization is refactored
(update-in [:teams id] merge team)
(assoc :permissions permissions)
;; FIXME: this is a redundant operation that only needed by
;; workspace; ti will not be needed after workspace
;; bootstrap & urls refactor
(assoc :current-team-id id)))
ptk/WatchEvent
(watch [_ _ _]
(rx/of (features/initialize (or features #{}))))
ptk/EffectEvent
(effect [_ _ _]
(swap! storage/global assoc ::current-team-id id))))
(defn- team-initialized
[]
[team-id]
(ptk/reify ::team-initialized
ptk/WatchEvent
(watch [_ state _]
(let [team-id (:current-team-id state)
teams (get state :teams)
team (get teams team-id)]
(let [teams (get state :teams)
team (get teams team-id)]
(if (not team)
(rx/throw (ex/error :type :authentication))
(rx/of (set-current-team team)
(fetch-members)))))))
(let [permissions (get team :permissions)
features (get team :features)]
(rx/of #(assoc % :permissions permissions)
(features/initialize (or features #{}))
(fetch-members team-id))))))))
(defn initialize-team
[team-id]
@@ -138,8 +118,7 @@
(rx/of (fetch-teams))
(->> stream
(rx/filter (ptk/type? ::teams-fetched))
(rx/observe-on :async)
(rx/map team-initialized)))
(rx/map (partial team-initialized team-id))))
(rx/take-until stopper))))))
(defn finalize-team
@@ -169,7 +148,7 @@
params (assoc params :team-id team-id)]
(->> (rp/cmd! :update-team-member-role params)
(rx/mapcat (fn [_]
(rx/of (fetch-members)
(rx/of (fetch-members team-id)
(fetch-teams)
(ptk/data-event ::ev/event
{::ev/name "update-team-member-role"
@@ -187,7 +166,7 @@
params (assoc params :team-id team-id)]
(->> (rp/cmd! :delete-team-member params)
(rx/mapcat (fn [_]
(rx/of (fetch-members)
(rx/of (fetch-members team-id)
(fetch-teams)
(ptk/data-event ::ev/event
{::ev/name "delete-team-member"

View File

@@ -291,7 +291,8 @@
(watch [_ state stream]
(let [features (features/get-team-enabled-features state)
render-wasm? (contains? features "render-wasm/v1")
stopper-s (rx/filter (ptk/type? ::finalize-workspace) stream)]
stopper-s (rx/filter (ptk/type? ::finalize-workspace) stream)
team-id (:current-team-id state)]
(->> (rx/concat
;; Firstly load wasm module if it is enabled and fonts
@@ -305,7 +306,7 @@
(rx/filter (ptk/type? ::df/fonts-loaded))
(rx/take 1)
(rx/ignore))
(rx/of (df/fetch-fonts)))
(rx/of (df/fetch-fonts team-id)))
;; Then fetch file and thumbnails
(->> (rx/zip (rp/cmd! :get-file {:id file-id :features features})
@@ -335,7 +336,7 @@
ptk/WatchEvent
(watch [_ state stream]
(log/debug :hint "initialize-workspace" :file-id file-id)
(log/debug :hint "initialize-workspace" :file-id (dm/str file-id))
(let [stoper-s (rx/filter (ptk/type? ::finalize-workspace) stream)
rparams (rt/get-params state)]

View File

@@ -210,42 +210,45 @@
(swap! storage/session dissoc :plugin-url))))))
(defn use-templates-import
[can-edit? template-url default-project-id]
(mf/with-layout-effect
[can-edit? template-url default-project-id]
(when (and (some? template-url) (some? default-project-id))
(if can-edit?
(let [valid-url? (and (str/ends-with? template-url ".penpot")
(str/starts-with? template-url cf/templates-uri))
template-name (when valid-url? (subs template-url (count cf/templates-uri)))
on-import #(st/emit! (dpj/fetch-files default-project-id)
(dd/fetch-recent-files)
(dd/fetch-projects)
(dd/clear-selected-files)
(ptk/event ::ev/event {::ev/name "install-template-from-link-finished"
:name template-name
:url template-url}))]
(if valid-url?
(do
(st/emit! (ptk/event ::ev/event {::ev/name "install-template-from-link" :name template-name :url template-url}))
(->> (http/send! {:method :get
:uri template-url
:response-type :blob
:omit-default-headers true})
(rx/subs!
(fn [result]
(if (or (< (:status result) 200) (>= (:status result) 300))
(st/emit! (notif/error (tr "dashboard.import.error")))
(st/emit! (modal/show
{:type :import
:project-id default-project-id
:entries [{:name template-name :uri (wapi/create-uri (:body result))}]
:on-finish-import on-import})))))))
(st/emit! (notif/error (tr "dashboard.import.bad-url")))))
(st/emit! (notif/error (tr "dashboard.import.no-perms"))))
[can-edit? template-url project]
(let [project-id (get project :id)
team-id (get project :team-id)]
(mf/with-layout-effect [can-edit? template-url project-id team-id]
(when (and (some? template-url)
(some? project-id)
(some? team-id))
(if can-edit?
(let [valid-url? (and (str/ends-with? template-url ".penpot")
(str/starts-with? template-url cf/templates-uri))
template-name (when valid-url? (subs template-url (count cf/templates-uri)))
on-import #(st/emit! (dpj/fetch-files project-id)
(dd/fetch-recent-files team-id)
(dd/fetch-projects team-id)
(dd/clear-selected-files)
(ptk/event ::ev/event {::ev/name "install-template-from-link-finished"
:name template-name
:url template-url}))]
(if valid-url?
(do
(st/emit! (ptk/event ::ev/event {::ev/name "install-template-from-link" :name template-name :url template-url}))
(->> (http/send! {:method :get
:uri template-url
:response-type :blob
:omit-default-headers true})
(rx/subs!
(fn [result]
(if (or (< (:status result) 200) (>= (:status result) 300))
(st/emit! (notif/error (tr "dashboard.import.error")))
(st/emit! (modal/show
{:type :import
:project-id project-id
:entries [{:name template-name :uri (wapi/create-uri (:body result))}]
:on-finish-import on-import})))))))
(st/emit! (notif/error (tr "dashboard.import.bad-url")))))
(st/emit! (notif/error (tr "dashboard.import.no-perms"))))
(binding [storage/*sync* true]
(swap! storage/session dissoc :template-url)))))
(binding [storage/*sync* true]
(swap! storage/session dissoc :template-url))))))
(mf/defc dashboard*
{::mf/props :obj}
@@ -270,10 +273,10 @@
(hooks/use-shortcuts ::dashboard sc/shortcuts)
(mf/with-effect []
(st/emit! (dd/initialize))
(mf/with-effect [team-id]
(st/emit! (dd/initialize team-id))
(fn []
(st/emit! (dd/finalize))))
(st/emit! (dd/finalize team-id))))
(mf/with-effect []
(let [key (events/listen goog/global "keydown"
@@ -285,7 +288,7 @@
(events/unlistenByKey key))))
(use-plugin-register plugin-url team-id (:id default-project))
(use-templates-import can-edit? template-url (:id default-project))
(use-templates-import can-edit? template-url default-project)
[:& (mf/provider ctx/current-project-id) {:value project-id}
[:> modal-container*]

View File

@@ -133,7 +133,7 @@
(st/emit! (dcm/go-to-dashboard-files
{:project-id project-id
:team-id team-id}))
(st/emit! (dd/fetch-recent-files)
(st/emit! (dd/fetch-recent-files team-id)
(dd/clear-selected-files))))
on-move-accept

View File

@@ -566,8 +566,9 @@
on-finish-import
(mf/use-fn
(mf/deps team-id)
(fn []
(st/emit! (dd/fetch-recent-files)
(st/emit! (dd/fetch-recent-files team-id)
(dd/clear-selected-files))))
import-files (use-import-file project-id on-finish-import)
@@ -608,9 +609,10 @@
on-drop-success
(mf/use-fn
(mf/deps team-id)
(fn []
(st/emit! (ntf/success (tr "dashboard.success-move-file"))
(dd/fetch-recent-files)
(dd/fetch-recent-files team-id)
(dd/clear-selected-files))))
on-drop

View File

@@ -344,11 +344,13 @@
continue-template
(mf/use-fn
(mf/deps on-finish-import)
(fn [template]
(let [on-success
(fn [_event]
(reset! status* :import-success)
(st/emit! (dd/fetch-recent-files)))
(when (fn? on-finish-import)
(on-finish-import)))
on-error
(fn [cause]
@@ -479,8 +481,6 @@
[:> import-entry* {:entry (assoc template :status status)
:can-be-deleted false}])]
;; (prn "import-dialog" status)
[:div {:class (stl/css :modal-footer)}
[:div {:class (stl/css :action-buttons)}
(when (= :analyze status)

View File

@@ -105,7 +105,8 @@
[{:keys [project is-first team files can-edit]}]
(let [locale (mf/deref i18n/locale)
project-id (:id project)
project-id (get project :id)
team-id (get team :id)
file-count (or (:count project) 0)
is-draft? (:is-default project)
@@ -191,11 +192,11 @@
on-import
(mf/use-fn
(mf/deps project-id)
(mf/deps project-id team-id)
(fn []
(st/emit! (dpj/fetch-files project-id)
(dd/fetch-recent-files)
(dd/fetch-projects)
(dd/fetch-recent-files team-id)
(dd/fetch-projects team-id)
(dd/clear-selected-files))))
handle-create-click
@@ -317,6 +318,8 @@
(sort-by :modified-at)
(reverse)))
team-id (get team :id)
recent-map (mf/deref ref:recent-files)
permisions (:permissions team)
@@ -327,7 +330,7 @@
show-team-hero* (mf/use-state #(get storage/global ::show-team-hero true))
show-team-hero? (deref show-team-hero*)
is-my-penpot (= (:default-team-id profile) (:id team))
is-my-penpot (= (:default-team-id profile) team-id)
is-defalt-team? (:is-default team)
on-close
@@ -346,8 +349,8 @@
(:name team))]
(dom/set-html-title (tr "title.dashboard.projects" tname))))
(mf/with-effect []
(st/emit! (dd/fetch-recent-files)
(mf/with-effect [team-id]
(st/emit! (dd/fetch-recent-files team-id)
(dd/clear-selected-files)))
(when (seq projects)

View File

@@ -37,6 +37,7 @@
[template team-id project-id default-project-id section]
(letfn [(on-finish []
(st/emit!
(dd/fetch-recent-files team-id)
(ptk/event ::ev/event {::ev/name "import-template-finish"
::ev/origin "dashboard"
:template (:name template)

View File

@@ -37,7 +37,7 @@
scale-enabled?
(mf/use-callback
(fn [export]
(#{:png :jpeg} (:type export))))
(#{:png :jpeg :webp} (:type export))))
in-progress? (:in-progress xstate)
@@ -123,6 +123,7 @@
format-options [{:value "png" :label "PNG"}
{:value "jpeg" :label "JPG"}
{:value "webp" :label "WEBP"}
{:value "svg" :label "SVG"}
{:value "pdf" :label "PDF"}]]

View File

@@ -51,7 +51,7 @@
.element-group {
display: grid;
grid-template-columns: repeat(8, 1fr);
grid-template-columns: repeat(9, 1fr);
column-gap: $s-4;
.action-btn {
@extend .button-tertiary;
@@ -64,13 +64,13 @@
}
.input-wrapper {
grid-column: span 7;
grid-column: span 8;
display: grid;
grid-template-columns: subgrid;
}
.format-select {
grid-column: span 2;
grid-column: span 3;
padding: 0;
.dropdown-upwards {

View File

@@ -8,6 +8,7 @@
(:require-macros [app.main.style :as stl])
(:require
[app.main.data.comments :as dcmt]
[app.main.data.event :as ev]
[app.main.data.workspace :as dw]
[app.main.data.workspace.comments :as dwcm]
[app.main.refs :as refs]
@@ -112,9 +113,11 @@
on-thread-click
(mf/use-fn
(mf/deps page-id)
(mf/deps page-id from-viewer)
(fn [thread]
(st/emit! (dwcm/navigate-to-comment thread))))]
(if from-viewer
(st/emit! (with-meta (dcmt/open-thread thread) {::ev/origin "viewer"}))
(st/emit! (dwcm/navigate-to-comment thread)))))]
[:div {:class (stl/css-case :comments-section true
:from-viewer from-viewer)}

View File

@@ -568,7 +568,6 @@
[{:keys [starting-tab file-id] :as props :or {starting-tab :libraries}}]
(let [files (mf/deref refs/files)
file (get files file-id)
team-id (:team-id file)
shared? (:is-shared file)
linked-libraries
@@ -616,8 +615,8 @@
:id "updates"
:content updates-tab}]]
(mf/with-effect [team-id]
(st/emit! (dtm/fetch-shared-files team-id)))
(mf/with-effect []
(st/emit! (dtm/fetch-shared-files)))
[:div {:class (stl/css :modal-overlay)
:on-click close-dialog-outside

View File

@@ -197,16 +197,25 @@
:id "inspect"
:content inspect-content}])]
(mf/with-effect [permissions]
(when-not (:can-edit permissions)
(on-change-tab :inspect)))
[:div {:class (stl/css :tool-window)}
[:> tab-switcher* {:tabs tabs
:default-selected "info"
:on-change-tab on-change-tab
:selected (name options-mode)
:class (stl/css :options-tab-switcher)}]]))
(if (:can-edit permissions)
[:> tab-switcher* {:tabs tabs
:default-selected "info"
:on-change-tab on-change-tab
:selected (name options-mode)
:class (stl/css :options-tab-switcher)}]
[:div {:class (stl/css-case :element-options true
:inspect-options true
:read-only true)}
[:& hrs/right-sidebar {:page-id page-id
:objects objects
:file-id file-id
:frame shape-parent-frame
:shapes selected-shapes
:on-change-section on-change-section
:on-expand on-expand
:from :workspace}]])]))
;; TODO: this need optimizations, selected-objects and
;; selected-objects-with-children are derefed always but they only

View File

@@ -33,6 +33,11 @@
padding-top: $s-8;
}
.read-only {
grid-template-areas: "right-sidebar";
padding: var(--sp-s);
}
.design-options,
.interaction-options {
overflow: auto;

View File

@@ -53,7 +53,7 @@
scale-enabled?
(mf/use-fn
(fn [export]
(#{:png :jpeg} (:type export))))
(#{:png :jpeg :webp} (:type export))))
on-download
(mf/use-fn
@@ -173,6 +173,7 @@
format-options [{:value "png" :label "PNG"}
{:value "jpeg" :label "JPG"}
{:value "webp" :label "WEBP"}
{:value "svg" :label "SVG"}
{:value "pdf" :label "PDF"}]]

View File

@@ -32,18 +32,18 @@
.element-group {
display: grid;
grid-template-columns: repeat(8, 1fr);
grid-template-columns: repeat(9, 1fr);
column-gap: $s-4;
}
.input-wrapper {
grid-column: span 7;
grid-column: span 8;
display: grid;
grid-template-columns: subgrid;
}
.format-select {
grid-column: span 2;
grid-column: span 3;
padding: 0;
.dropdown-upwards {

View File

@@ -231,7 +231,7 @@
show-selrect? (and selrect (empty? drawing) (not text-editing?))
show-measures? (and (not transform)
(not node-editing?)
(or show-distances? mode-inspect?))
(or show-distances? mode-inspect? read-only?))
show-artboard-names? (contains? layout :display-artboard-names)
hide-ui? (contains? layout :hide-ui)
show-rulers? (and (contains? layout :rulers) (not hide-ui?))

View File

@@ -261,7 +261,7 @@
:hidden hidden})))
;; export interface Export {
;; type: 'png' | 'jpeg' | 'svg' | 'pdf';
;; type: 'png' | 'jpeg' | 'webp' | 'svg' | 'pdf';
;; scale: number;
;; suffix: string;
;; }

View File

@@ -243,7 +243,7 @@
;; export interface Export {
;; type: 'png' | 'jpeg' | 'svg' | 'pdf';
;; type: 'png' | 'jpeg' | 'webp' | 'svg' | 'pdf';
;; scale: number;
;; suffix: string;
;; }

View File

@@ -14,7 +14,6 @@
[app.common.types.components-list :as ctkl]
[app.common.uri :as u]
[app.main.data.fonts :as df]
[app.main.data.team :as dtm]
[app.main.features :as features]
[app.main.render :as render]
[app.main.repo :as repo]
@@ -30,6 +29,20 @@
(log/setup! {:app :info})
(defn set-current-team
[{:keys [id permissions features] :as team}]
(ptk/reify ::set-current-team
ptk/UpdateEvent
(update [_ state]
(-> state
(assoc :permissions permissions)
(update :teams assoc id team)
(assoc :current-team-id id)))
ptk/WatchEvent
(watch [_ _ _]
(rx/of (features/initialize (or features #{}))))))
(defn- fetch-team
[& {:keys [file-id]}]
(ptk/reify ::fetch-team
@@ -37,7 +50,7 @@
(watch [_ _ _]
(->> (repo/cmd! :get-team {:file-id file-id})
(rx/mapcat (fn [team]
(rx/of (dtm/set-current-team team)
(rx/of (set-current-team team)
(ptk/data-event ::team-fetched team))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;