Implement penpot_instance_component with id remapping #207
Labels
No labels
bug
chore
documentation
enhancement
feature
ready
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
david/pi-extensions-and-skills#207
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Implement
penpot_instance_component: place a real Penpot component instance into a frame by cloning the component's main-instance shape subtree with fresh ids and correct parent wiring, so the result stays linked to the library component rather than being a detached copy.Background
Depends on: #206
The field contract was validated end to end on Penpot 2.17 — the schema risk is retired and what remains is careful id remapping:
data.components(withmainInstanceId= the shape id of the frame that is the main instance, andmainInstancePage= its page). The main-instance marking lives on the component entry, not on the shape.add-objthe root as a frame carryingcomponentId: <component id>andcomponentRoot: true; thenadd-objeach child withshapeRef: <the corresponding main-instance child's shape id>.typeand geometry; ids are freshly generated andparentId/frameIdare rewired to the new instance's ids.update-filecall this is atomic — which the staged-changeset architecture already provides.Nested components (a component whose main instance itself contains an instance) are the known edge: handle a single level correctly and, if deeper nesting is out of scope, refuse with a clear message rather than silently producing a broken half-linked tree.
Documentation Required
A separate process downloads these into the listed folders before this issue is implemented. Check the folders for the actual reference material before starting.
docs/reference/penpot-api/mainInstanceId,mainInstancePage,path,name, and how a component is identified.componentId,componentRoot,shapeRef, plus the full shape payload the root and children must still carry.data.components).shapesarrays, for the remapping step.<PENPOT_URL>/api/main/doc/openapi.json—get-file(to read the main instance's page objects) andupdate-file/add-obj.docs/reference/pi-coding-agent/anddocs/reference/typebox/details.Implementation Details
Create
extensions/penpot/src/instance.ts(pure remapping — the unit-tested core) andextensions/penpot/src/tools/instance.ts, and register the tool.src/instance.tsbuildInstance({ component, mainInstanceShapes, rootId, x, y, parentId, pageId })→ an ordered list ofadd-objchanges:id,parentId,frameId, and each frame'sshapesarray entries.frame,componentId= the component id,componentRoot: true, positioned atx/y, parented to the target frame.shapeRef= the main-instance child's original shape id, with its owntypeand geometry, andparentId/frameIdremapped.fillColorRefId/fillColorRefFile,typographyRefId/typographyRefFile) unchanged — they point at library assets, not at the instance.update-fileapplies changes in order).collectDescendants(objects, rootId)— walk a page'sobjectsmap into a parent→children-ordered list, and fail loudly on a cycle or a missing child link.detectNestedInstance(shapes)— returns the offending shape id(s) if the subtree contains another instance root; used to refuse rather than half-build.src/tools/instance.ts—penpot_instance_componentParameters:
file_id(required — the consumer file being written),component_name(required; resolved to a component id via the linked-library index),page_id(optional),parent_id(required — the frame the instance is placed in),x,y(required),instance_name(optional; defaults to the component name).Behaviour:
component_nameagainst the consumer file's components (including those from linked libraries). Missing or ambiguous → stage nothing, return the resolution error with candidates.componentFileId, or the linked library that owns it) and fetch the main instance's page objects.detailsplus a preview line naming the component.penpot_commitapplies the instance atomically with whatever else is staged.Acceptance Criteria
add-objfor the root withcomponentIdset andcomponentRoot: true, and oneadd-objper descendant withshapeRefpointing at the corresponding main-instance shape id.parentId/frameIdand frameshapesarrays are fully remapped to the new ids.update-filecan apply them in order.fillColorRefId/fillColorRefFile,typographyRefId/typographyRefFile) are preserved unchanged.x/yinsideparent_id, not at the main instance's original coordinates.penpot_link_library.Test Plan
Live validation (requires
PENPOT_URL/PENPOT_TOKEN, scratch project with the imported library linked to a consumer file):penpot_instance_componentfor the Button component inside it; commit.pi-loop opened and merged a pull request for this issue: #240