23651 Commits

Author SHA1 Message Date
Andrés Moya
c63bf0a5e0 🎉 Allow the user to choose the tokens-source of a file 2026-09-11 13:28:08 +02:00
Andrés Moya
ff64aa4379 🔧 Rename :tokens-file to :tokens-source 2026-09-11 13:28:08 +02:00
Andrés Moya
ebada87e77 🔧 Normalize status helper functions and access token sets by id 2026-09-11 13:28:08 +02:00
Andrés Moya
bbed96d4c8 🔧 Make all status operations use the new data structure 2026-09-11 13:28:08 +02:00
Andrés Moya
d0e3a6581d 🔧 Add separated TokenStatus to store status apart of TokensLib 2026-09-11 13:28:08 +02:00
Andrés Moya
f61ea521dc 🔧 Refactor tokens-lib initialization 2026-09-11 13:28:08 +02:00
Andrés Moya
cfeccae017 Auto link tokens when adding external libraries (provisional) 2026-09-11 13:28:08 +02:00
Alonso Torres
fc8c5a98de
🐛 Fix handler change to equal (#11612) 2026-09-11 13:23:45 +02:00
Andrey Antukh
bda8459d89 Merge remote-tracking branch 'origin/staging' into develop 2026-09-11 12:57:51 +02:00
Andrey Antukh
06239844b1
🐛 Fix chunked upload storage amplification and cap chunk size (#11635)
* 🐛 Reject duplicate chunk index in chunked uploads

Repeat uploads of the same chunk index each stored a new
object because upload-chunk only checked index bounds. Run the
handler in a transaction, lock the session row and reject an
already-stored index with :duplicate-chunk-index.

Also harden assemble-chunks to require exactly indices 0..n-1
 so gaps or duplicates fail instead of assembling a corrupt
file. Covers media, fonts and binfile through the shared
helper.

Closes #11634

AI-assisted-by: muse-spark-1.3-contributor

*  Cap upload chunk size at 30 MiB by default

Chunks were only bounded by the 350 MiB HTTP body limit while the
30 MiB caps applied to the assembled file. Add :upload-max-chunk-size
(default 30 MiB, tunable via env) and reject oversize chunks in
upload-chunk with :validation/:chunk-too-large before anything is
stored. App clients slice at 25/10 MiB, so no frontend change needed.

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Fix tx-run! call and storage resolve in upload-chunk

Pass cfg as first arg to db/tx-run!, which expects [system f & params; without it every chunk upload raised invalid system/cfg provided and no chunk was stored, breaking assemble with missing-chunks. Also resolve storage without reuse-conn: put-object! writes to the backend outside any transaction, so reusing the tx connection gives no atomicity. Media, font and storage suites green, lint and format clean. AI-assisted-by: muse-spark-1.3-contributor
2026-09-11 12:10:57 +02:00
Andrey Antukh
09736aa4c9 Enforce commit body line wrapping
Add a body line-length validator to scripts/check-commit. It
fails when a body line exceeds 76 characters, exempting
trailers, URLs, and unbreakable tokens. The 76 limit leaves
room for git log's four-space indent in an 80-column
terminal.

Align the subject limit with the documented 70 characters;
the checker allowed 90 before.

Document the rule as a hard, verifiable requirement in
AGENTS.md, CONTRIBUTING.md, the create-commit skill, and
the workflow memory, and point at scripts/check-commit.

Add tests for the validator and the subject length rule.

AI-assisted-by: deepseek-flash
2026-09-11 08:10:49 +00:00
Alejandro Alonso
ffad71bdc5
🐛 Hard-clip tile atlas blit to avoid seam hairlines (#11639)
AA clip on the Current→atlas blit softens shared tile edges so
the canvas background shows through as 1px lines at the 512px
grid when tiles are composed with SrcOver.

Closes #11638
2026-09-11 09:22:36 +02:00
Elena Torró
32ed9b5a08
🐛 Fix board clip on drag (#11620) 2026-09-11 08:27:11 +02:00
Elena Torró
0913545b41
🐛 Fix letter spacing and position when flattening text to path (#11555)
* 🐛 Fix letter spacing and position when flattening text to path

* ♻️ Simplify text to path conversion and add flatten tests
2026-09-11 08:20:22 +02:00
Andrey Antukh
37dab75e1a Merge remote-tracking branch 'origin/staging' into develop 2026-09-10 20:29:44 +02:00
Andrey Antukh
f9c02926b9 Merge remote-tracking branch 'origin/main' into staging 2026-09-10 20:21:41 +02:00
bameda
bae3900537 ♻️ Rebalance CI runners and drop pinned ubuntu-24.04
Move build-docker and build-docker-devenv jobs from penpot-extended-runner
to penpot-standar-runner, point tests-exporter at the canonical
penpot-extended-runner label instead of the stale penpot-runner-02 alias,
and switch build-tag/release notify jobs from ubuntu-24.04 to ubuntu-latest.

Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-09-10 19:23:13 +02:00
bameda
757a5bd479 ♻️ Rebalance CI runners and drop pinned ubuntu-24.04
Move build-docker and build-docker-devenv jobs from penpot-extended-runner
to penpot-standar-runner, point tests-exporter at the canonical
penpot-extended-runner label instead of the stale penpot-runner-02 alias,
and switch build-tag/release notify jobs from ubuntu-24.04 to ubuntu-latest.

Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-09-10 19:22:46 +02:00
bameda
9c07dd124a ♻️ Rebalance CI runners and drop pinned ubuntu-24.04
Move build-docker and build-docker-devenv jobs from penpot-extended-runner
to penpot-standar-runner, point tests-exporter at the canonical
penpot-extended-runner label instead of the stale penpot-runner-02 alias,
and switch build-tag/release notify jobs from ubuntu-24.04 to ubuntu-latest.

Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-09-10 19:14:55 +02:00
makesomethingshit
99c036feac
🐛 Close nitrate modal when navigating to current plan (#11615)
Signed-off-by: makesomethingshit <junsoo1172@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-09-10 18:17:11 +02:00
Alejandro Alonso
aa78ca0be8
🐛 Keep text image fills aligned during drag (#11610)
Cached Skia paragraphs bake absolute image/gradient shaders at layout
time. On move, clones reuse those paragraphs while painting at the new
selrect, so glyphs move and the fill stays put. Record the paint origin
when layout is built and translate the canvas when painting from cache
so shaders track the text. Also sync bounds before update_layout so
fills bake against the current container.
2026-09-10 16:55:45 +02:00
Andrey Antukh
8952d70fd2
Optimize get-profiles-for-file-comments query (#11622)
Rewrite sql:file-comment-users to join comment with
comment_thread and union the requesting profile id, then
join the resulting small id set against profile.

The previous "id IN (subquery) OR id = ?" forced a
sequential scan over the whole profile table with a hashed
subplan filter, taking ~1.9s on large instances. The
semi-join lets the planner use profile_pkey, dropping the
query to sub-millisecond time. UNION (not UNION ALL) keeps
the previous dedup semantics when the requesting profile is
also a commenter.

AI-assisted-by: deepseek-flash
2.18.0-RC5
2026-09-10 16:45:22 +02:00
Andrey Antukh
7c27ed812a
♻️ Consolidate HIGHLIGHTS.md into CHANGES.md 🚀 section (#11531)
* ♻️ Consolidate HIGHLIGHTS.md into CHANGES.md 🚀 section

Eliminate the redundant HIGHLIGHTS.md file and make CHANGES.md
the single source of truth for version highlights.

- Add 🚀 section for 2.15.0 (MCP server integration)
- Add 4 missing highlight entries to 2.17.0 🚀 section
- Rewrite frontend parser to extract from CHANGES.md 🚀
  subsections instead of flat HIGHLIGHTS.md format
- Decouple parse-latest-released-version from highlights
  extraction so it works independently of 🚀 content
- Conditionally render highlights section in modal when non-empty
- Rewrite tests for new parser behavior (11 tests, 21 assertions)
- Delete HIGHLIGHTS.md and remove .gitignore exception
- Add step 8b to update-changelog skill for proactively
  proposing highlights during release workflows
- Add missing-highlights and missing-highlight-reference
  anomaly types to the changelog anomaly report script

Closes #11530

AI-assisted-by: qwen3.7-plus

* ♻️ Use consistent string library and add multi-version test

Address code review findings:

- Use str/split (cuerdas) consistently in extract-rocket-items
  instead of mixing cstr/split (clojure.string)
- Add parse-highlights-extracts-multiple-versions test to verify
  the parser correctly extracts 🚀 items from multiple
  versions in a single CHANGES.md body

AI-assisted-by: qwen3.7-plus

* ♻️ Scope 🚀 checks to X.Y.0 and split gaps from anomalies

Type C now only checks released X.Y.0 versions, since patches never carry 🚀 subsections by design. Type D requires both issue AND PR references with exact format, accepting multi-PR entries. C/D are reported as highlight gaps in their own section and no longer count toward the anomaly total. Key Principles and anomaly definitions updated to match. Addresses review comments on PR #11531.

AI-assisted-by: muse-spark-1.3-contributor

*  Render markdown links and bold in check-updates highlights

The highlights modal showed raw markdown from CHANGES.md 🚀 lines (brackets and URLs). Add a pure parse-highlight-item parser for inline links and bold, render fragments with literal hiccup in the modal (links open in a new tab), and style links and strong elements. Non-http URLs and malformed markup degrade to plain text. Adds 12 unit tests.

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Point full changelog link to main instead of staging

The view-changelog button in the check-updates modal linked to the staging branch. Point it to main, which holds the published changelog. Version detection still fetches from staging.

AI-assisted-by: muse-spark-1.3-contributor
2026-09-10 16:40:32 +02:00
Andrey Antukh
4ce459d720
🐛 Escape LDAP filter values and use directory email in retrieve-user (#11085)
Fix LDAP injection vulnerability (T5-N1-03) where the client-supplied email was used directly in the LDAP search filter without escaping RFC 4515 special characters (*, (, ), \, NUL), and the profile email was taken from client input instead of the LDAP directory attribute.

Changes:
- Add escape-ldap-filter-value per RFC 4515 section 3
- Apply escaping in search-user before building LDAP filter
- Add get-attr helper for multi-valued LDAP attributes
- Fix retrieve-user to use directory email (attrs-email) instead of client email
- Use cuerdas.core instead of clojure.string

Closes #11084

AI-assisted-by: mimo-v2.5-pro
2026-09-10 16:39:35 +02:00
Shlok Goyal
286ccb03fa
🐛 Preserve stroke dash and gap values on color change (#11557)
Signed-off-by: Shlok Goyal <shlokgoyal1279@gmail.com>
2026-09-10 14:36:51 +02:00
Alejandro Alonso
dbe5941a23
Export layer blur to WASM SVG (#11580)
SkSVGDevice drops paint image-filters, so re-emit visible layer blur as a
native feGaussianBlur filter on the composite <g>. Match canvas sigma via
radius_to_sigma(value * scale), and skip Skia blur filters on the SVG
VectorRenderer path so shapes do not vanish.

Closes #11380
2026-09-10 14:25:47 +02:00
makesomethingshit
30849babcc
🐛 Fix fontFamilies token property mapping in Plugin API (#11566)
* 🐛 Fix fontFamilies token property mapping in Plugin API

The Plugin API exposes the font-family token property as `fontFamilies`,
while Penpot stores the canonical applied-token attribute as
`:font-family`. The bidirectional plugin/internal attribute map did not
contain that alias, so explicit `applyToken(..., ["fontFamilies"])`
validation rejected the property and applied-token readback exposed the
undocumented singular `fontFamily`.

Add `:font-family -> :font-families` to the existing canonical alias
map. The reverse mapping is derived automatically, keeping application
and readback symmetric without introducing a font-specific code path.

Closes #11405

AI-assisted-by: Omen Alpha
Signed-off-by: 최준수 <junsoo1172@gmail.com>

* 🐛 Fix fontFamilies e2e test to target a text shape

The fontFamilies end-to-end regression created a flex layout frame,
whose attribute set (frame-with-layout-attributes) excludes
:font-family. The workspace token application filters such shapes,
so the internal binding and readback assertions would pass vacuously
without exercising the alias.

Target an actual `:text` shape (ctho/add-text) instead, so the test
verifies the full JS "fontFamilies" -> schema -> alias -> canonical
:font-family -> camelCase readback path.

AI-assisted-by: Omen Alpha
Signed-off-by: 최준수 <junsoo1172@gmail.com>

* 🐛 Fix fontFamilies test WASM error and add changelog entry

The text-shape fontFamilies e2e applies a layout-affecting token via
wasm renderer path, hitting missing WASM exports under Node. Merge
thw/setup-wasm-mocks! into the :each fixture and add plugins
CHANGELOG entry for the fontFamilies alias fix.

AI-assisted-by: muse-spark-1.3-contributor
Related to #11566

Signed-off-by: makesomethingshit <junsoo1172@gmail.com>

---------

Signed-off-by: 최준수 <junsoo1172@gmail.com>
Signed-off-by: makesomethingshit <junsoo1172@gmail.com>
2026-09-10 13:43:41 +02:00
Alejandro Alonso
dc12f1db91
Export image-filled strokes to WASM SVG as linked images (#11559)
Closes #11384

Skia's SVG backend drops save_layer+SrcIn, so image strokes are re-emitted
as a linked <image> clipped to an opaque stroke silhouette (filled outline,
clip-rule evenodd). Open-path caps join the silhouette and grow the image
dest by cap_bounds_margin so markers stay textured.
2026-09-10 13:29:14 +02:00
Luis de Dios
c589563912
♻️ Replace digit with number in password validations (#11609) 2026-09-10 12:15:58 +02:00
andrés gonzález
0eb3179016
💄 Adjust release notes 2.18 titles (#11608) 2.18.0-RC4 2026-09-10 11:50:57 +02:00
Andrey Antukh
d1ebf4cda2 📎 Update changelog 2026-09-10 10:41:10 +02:00
Andrey Antukh
fdb9e97572 📎 Update planner skill and AGENTS.md 2026-09-10 10:12:09 +02:00
Elenzakaleidos
9cd3b63eea
📚 Update README.md (#11602)
Added a new section for Penpot Enterprise detailing its features and benefits for organizations.

Signed-off-by: Elenzakaleidos <elena.scilinguo@kaleidos.net>
2.18.0-RC3
2026-09-10 09:52:06 +02:00
David Barragán Merino
94555c027e 🔧 Sync .github/workflows with develop
Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-09-10 09:47:10 +02:00
Elena Torró
9b24907992
🐛 Fix render text-decoration on spans and update regression tests (#11583) 2026-09-10 09:42:09 +02:00
Juan de la Cruz
b283d952a8
Add new slides content for 2.18 release (#11222)
*  Add new slides content for 2.18 release

* ♻️ Use buttons from DS

* ♻️ Use new SCSS guidelines

* ♻️ Use a base stylesheet for all version files

* ♻️ Use new SCSS guidelines

*  Add new images and wording

---------

Co-authored-by: Luis de Dios <luis.dedios@kaleidos.net>
2026-09-10 09:35:34 +02:00
andrés gonzález
6f63a5fcbf
Persist hide resolved comments preference (#10694)
Store the hide-resolved filter in user storage and restore it when
entering the workspace or viewer, consistent with canvas comment
visibility from #10239. Match the comments filter separator styling to
the main menu and add the missing mentions option in the viewer
dropdown.

Closes #10686

Signed-off-by: Andres Gonzalez <andres.gonzalez79@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 08:14:37 +02:00
Elena Torró
61caefbcf3
🔧 Support independent image bounds on wasm export (#11590) 2026-09-09 18:27:22 +02:00
Elena Torró
6586631293
🐛 Fix wasm boolean paths with opposite winding operand (#11551) 2026-09-09 18:19:29 +02:00
Andrey Antukh
66fb4a69ba 📎 Update copyright headers 2026-09-09 17:58:09 +02:00
Alejandro Alonso
ac84557740
🐛 Re-upload WASM text after WebGL context restore (#11589)
During reload-renderer!, reloading? keeps initialized?/ready? false
while set-objects runs (especially the sync path for small files).
Text content used that guard and was skipped; geometry already used
live?. Gate use-shape, has-shape, and set-shape-text-content on
wasm/live? so text is restored with the rest of the shapes.
2.18.0-RC2
2026-09-09 17:25:18 +02:00
Elena Torró
3d4a5ca2aa
🔧 Use mutex so only one test owns global at a time (#11585) 2026-09-09 17:10:04 +02:00
Danny Shirely
d45c6710b7
🎉 Implement independent image bounds resizing (#11430)
* 🎉 Implement independent image bounds resizing

Add canvas resize interaction mode that allows users to resize an
image object's bounding box independently from the underlying bitmap
content without scaling or distortion while holding the Mod key.

AI-assisted-by: gemini-2.5-pro

* ♻️ Address reviewer feedback from elenatorro

- Remove legacy cfh/image-shape? check in shape-has-image-fill?
- Guard bounds-resize with positive dimensions instead of clamping scalev to preserve flipping
- Remove :metadata from transform-attrs in modifiers.cljs
- Restore preserveAspectRatio logic based on keep-ar? in fills.cljs
- Compute source rect against destination rect for raster and SVG fills in WASM renderer

* 🔧 Fix clippy needless borrow warnings in wasm image fills

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-09-09 15:16:56 +02:00
Alonso Torres
bf2fcba3be
🐛 Fix problems with pixel precission in WASM renderer (#11560) 2026-09-09 15:11:58 +02:00
Andrey Antukh
43f7e49aa0 📎 Update changelog 2026-09-09 11:48:37 +02:00
Andrey Antukh
d6e82f44a6
Enforce absolute maximum session duration (#11447)
Session tokens now carry an :exp claim anchored to created-at (not
modified-at), so activity cannot extend the session beyond the
absolute maximum (default 30 days, configurable via
PENPOT_AUTH_TOKEN_COOKIE_MAX_AGE_ABSOLUTE). The existing token
verification already rejects expired tokens, so enforcement is
automatic. Also extends the GC task to purge expired
http_session_v2 rows, which were previously never cleaned up.

Closes #11444

AI-assisted-by: longcat-2.0
2026-09-09 11:45:28 +02:00
Andrey Antukh
eca1d81692 🔧 Remove legacy pnpm build key and clarify updating doc
Drop the ignored-since-pnpm-11 onlyBuiltDependencies entry from
render-wasm/pnpm-workspace.yaml, keeping allowBuilds as the single
source of build approvals. Clarify the updating-pnpm gotcha so it no
longer claims pnpm writes ignoredBuiltDependencies.

AI-assisted-by: muse-spark-1.3-contributor
2026-09-09 11:45:00 +02:00
Pablo Alba
d263c23a58
🐛 Add ssrf check for nitrate sso and add timeouts to http client (#11576) 2026-09-09 11:23:53 +02:00
Alejandro Alonso
e67ebacef2
🐛 Export svg-raw shapes through WASM SVG draw_svg (#11567)
SVG export treated SVGRaw like a fill/stroke leaf and never called
draw_svg, so imported SVG content exported as an empty document.

Relates to #10546
2026-09-09 11:16:01 +02:00
Andrey Antukh
91860f6916 📎 Update changelog 2026-09-09 11:12:57 +02:00