From 07ad152ae5a264d24fe2707c47219d3a17bc3745 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Tue, 28 Apr 2026 17:24:06 +0200 Subject: [PATCH] :bug: Fix .component() returning outermost component for nested instances The shape API method .component() used locate-component which walks to the outermost instance root via get-instance-root. For nested component instances (e.g. a button inside a card), this incorrectly returned the outer component (the card) instead of the nearest one (the button). Added locate-head-component in utils.cljs which uses get-head-shape to find the nearest component head, and updated the :component property in shape.cljs to use it. Fixes #9183 --- CHANGES.md | 1 + frontend/src/app/plugins/shape.cljs | 4 ++-- frontend/src/app/plugins/utils.cljs | 11 +++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ce033171a8..149fc49f05 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -130,6 +130,7 @@ - Fix internal error on layer prev/next sibling selection (by @jsdevninja) [Github #9003](https://github.com/penpot/penpot/issues/9003) - Fix tooltip appearing two times when nested elements [Github #9031](https://github.com/penpot/penpot/issues/9031) - Fix broken update library notification link in the UI [Github #9070](https://github.com/penpot/penpot/issues/9070) +- Fix plugin API `ShapeBase.component()` returning the outermost component instead of the immediate component in case of nested component instances [Github #9183](https://github.com/penpot/penpot/issues/9183) ## 2.15.0 (Unreleased) diff --git a/frontend/src/app/plugins/shape.cljs b/frontend/src/app/plugins/shape.cljs index da09c90ea8..63037e405c 100644 --- a/frontend/src/app/plugins/shape.cljs +++ b/frontend/src/app/plugins/shape.cljs @@ -1186,8 +1186,8 @@ (let [objects (u/locate-objects file-id page-id) shape (u/locate-shape file-id page-id id)] (when (ctn/in-any-component? objects shape) - (let [[root component] (u/locate-component objects shape)] - (lib-component-proxy plugin-id (:component-file root) (:id component)))))) + (when-let [[head component] (u/locate-head-component objects shape)] + (lib-component-proxy plugin-id (:component-file head) (:id component)))))) :detach (fn [] diff --git a/frontend/src/app/plugins/utils.cljs b/frontend/src/app/plugins/utils.cljs index 19de73fcde..81dfb6cb82 100644 --- a/frontend/src/app/plugins/utils.cljs +++ b/frontend/src/app/plugins/utils.cljs @@ -100,6 +100,17 @@ root (ctn/get-instance-root objects shape)] [root (ctf/resolve-component root file libraries {:include-deleted? true})])) +(defn locate-head-component + "Like locate-component but resolves via the nearest component head + instead of the outermost instance root." + [objects shape] + (let [state (deref st/state) + file (dsh/lookup-file state) + libraries (dsh/lookup-libraries state) + head (ctn/get-head-shape objects shape)] + (when head + [head (ctf/resolve-component head file libraries {:include-deleted? true})]))) + (defn proxy->file [proxy] (let [id (obj/get proxy "$id")]