23246 Commits

Author SHA1 Message Date
Alejandro Alonso
409a1a17c7
Revert "🐛 Fix text creating on draft.js (#11086)"
This reverts commit 6df045b194b6393553f7b081f5476916ec1b4ab0.
2026-08-05 13:21:33 +02:00
Eva Marco
6df045b194
🐛 Fix text creating on draft.js (#11086) 2026-08-05 13:10:21 +02:00
Belén Albeza
1b26b69b25
🐛 Fix Firefox not inserting emoji from MacOS Character Viewer (#11072) 2026-08-05 12:53:20 +02:00
Andrey Antukh
6f2bfb617c Merge remote-tracking branch 'origin/staging' into develop 2026-08-05 10:16:06 +02:00
Andrey Antukh
636bc22cc4
Add Node.js E2E API tests for backend (#10787)
Add end-to-end HTTP tests under backend/test/e2e/ using Node.js built-in
test runner (node:test) and native fetch. Tests run through the devenv
nginx proxy on port 3450.

Test suites (19 tests total):
- auth-flow: demo profile creation, login, session cookies, access tokens
- export-binfile: file creation, export to asset URL via SSE
- asset-download: download with cookie/token auth, 401 without auth,
  S3 redirect behavior, full export-to-download flow

Key findings documented in tests:
- nginx @handle_redirect intercepts backend 307 and proxies to S3 directly,
  stripping the client Authorization header (bug does not reproduce in devenv)
- SSE end event uses ~#uri tagged format for URLs
- Unauthenticated RPC returns uuid/zero profile (not null)

AI-assisted-by: mimo-v2.5-pro

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-08-05 10:15:41 +02:00
Andrey Antukh
aeedb96260
Add media-processor service for image and font processing (#10767)
*  Add media-processor service for image and font processing

Externalizes ImageMagick and FontForge subprocess invocations into a
separate Node.js HTTP service (media-processor/). Backend dispatches
via feature flag :use-remote-media-processing.

Key changes:
- media-processor module (TypeScript, Express 5, Sharp, FontForge/woff)
  - POST /api/image/info, /api/image/thumbnail, /api/font/generate
  - Resource limits: 128MP rejection, prlimit (512MB + 30s CPU)
  - Streaming multipart via SequenceInputStream
- app.media split into validation (leaf), local (shell impls), remote (HTTP)
- Schema enforcement: :upload and :input schemas in validation namespace
- Configurable timeout (PENPOT_MEDIA_PROCESSING_SERVICE_TIMEOUT)
- 78 tests across 4 files (image, font, middleware, config)
- FontForge path escaping for command injection prevention
- Parallel font variant conversions with Promise.all

AI-assisted-by: mimo-v2.5-pro

* 🐳 Revert docker-compose changes from media-processor commit

Remove docker-compose.yaml modifications that were part of the media-processor
service commit. The media-processor service definition, flags, and environment
variables are reverted to their previous state.

AI-assisted-by: qwen3.7-plus

* ⬆️ Update dependencies

* 🐛 Fix PR review issues in media-processor

- Font path bug: sfntToWoff and woff2ToSfnt now copy input to temp dir
  when input is a file path, ensuring output lands in expected location
- Error preservation: execCommand preserves killed/signal/code properties
  from child process errors for OOM detection
- Content-Length: service-multipart-request calculates and includes
  Content-Length header for streaming multipart requests

AI-assisted-by: qwen3.7-plus

* 🐛 Fix code review issues in media-processor

- Rename PENPOT_MEDIA_PROCESSOR_SECRET_KEY to PENPOT_MEDIA_PROCESSOR_SHARED_KEY
  in devenv to match backend config key
- Fix timeout middleware to destroy request AFTER response finishes,
  preventing truncated 504 responses
- Fix quality=0 parsing to preserve explicit zero (was silently overridden to 85)
- Replace require('fs') with proper ES module import in upload-storage.ts
- Refactor font conversion temp-dir boilerplate into withTempInput helper
- Document FontForge escaping limitations (single quotes only)
- Fix misleading comment in image.ts about sharp metadata decoding

AI-assisted-by: qwen3.7-plus

* 🐛 Fix code review issues in media-processor (round 2)

- Fix queue middleware to skip next() when response already ended,
  preventing orphaned work after timeout
- Fix hybrid storage to use disk when Content-Length is absent (chunked
  transfer), preventing unbounded memory allocation
- Add source image format validation in generateThumbnail to reject
  unsupported formats (TIFF, BMP, etc.) with 400 instead of 500
- Remove dead code in convertFont for unreachable woff→woff path
- Remove unused isEnabled() method from LokiLogTransport
- Fix sfntToWoff to use correct extension (.ttf/.otf) based on source type
- Extract queue middleware to separate file for testability
- Add comprehensive tests for queue middleware and upload storage

AI-assisted-by: qwen3.7-plus

* 🐛 Fix code review issues in media-processor (round 3)

- Fix disk-backed upload cleanup after successful requests by adding
  cleanup middleware that removes temp files on response finish/close
- Wrap sharp metadata/decoding errors as 400 validation errors instead
  of 500 internal errors
- Only apply flatten() for JPEG output to preserve alpha channel in
  PNG and WebP outputs

AI-assisted-by: qwen3.7-plus

*  Add comprehensive tests for media-processor

Phase 1 - Cleanup verification:
- Add cleanup middleware unit tests (6 tests)
- Add HTTP upload cleanup integration tests (5 tests)

Phase 2 - Error handling & alpha preservation:
- Add sharp error wrapping tests (4 tests)
- Add HTTP malformed image tests (2 tests)
- Add alpha preservation tests (3 tests)

Phase 3 - Edge cases:
- Add upload storage edge case tests (3 tests)
- Add queue middleware edge case tests (4 tests)

Phase 4 - Backend mock verification:
- Fix backend mocks to include :mtype field in image info responses
- Verify all error codes match actual service behavior

Total: 27 new tests added (160 tests passing)

AI-assisted-by: qwen3.7-plus

* 🐛 Fix code review issues in media-processor (round 4)

- Add Zod validation constraints for config values (int, positive, min)
- Fix auth middleware to compare Buffer byte lengths instead of string lengths
- Validate requested output dimensions in generateThumbnail (crop mode)
- Change queue middleware to release slot via callback in finally block
- Add comprehensive tests for all fixes

AI-assisted-by: qwen3.7-plus

* 🐛 Close HTTP response streams in backend media remote

- Wrap stream consumption in try/finally with .close() calls
- Add tests to verify stream closure for info, font-convert, and thumbnail

AI-assisted-by: qwen3.7-plus

* 🐛 Fix queue slot leak on upload failures

Make releaseQueue idempotent and attach fallback listener to release
slot when response finishes. This covers Multer errors that bypass
the route handler's finally block, preventing permanent queue stall.

AI-assisted-by: qwen3.7-plus

* 🐛 Cancel processing on timeout

Create AbortController in timeout middleware and abort signal when
timeout fires. Pass signal to Sharp and FontForge to cancel ongoing
processing and release resources when request is cancelled.

AI-assisted-by: qwen3.7-plus

* 🐛 Fix code review issues in media-processor (round 6)

- Error handler: check headersSent before writing response to prevent
  ERR_HTTP_HEADERS_SENT when timeout already sent 504
- Timeout config: increase default requestTimeout from 60s to 180s to
  match font processing timeout (120s) and backend request timeout
- Image processing: check abort signal before starting Sharp operations
  to cancel processing when timeout fires
- Queue lifecycle: remove res.on('close', release) fallback to hold
  queue slot until processing completes, preventing concurrency limit
  violation when client disconnects

AI-assisted-by: qwen3.7-plus

* 🐛 Close HTTP response stream in download-image

Wrap response body in with-open to ensure stream is closed after
writing to temp file, preventing HTTP connection leaks on repeated
URL imports.

AI-assisted-by: qwen3.7-plus

* 🐛 Close HTTP response stream on validation errors in download-image

Move with-open to wrap the entire validation and processing block,
ensuring the response body stream is closed even when validation fails
(non-2xx status, missing size, invalid media type). This prevents
HTTP connection leaks on repeated failed downloads.

Add test to verify stream closure on validation errors.

AI-assisted-by: qwen3.7-plus

* 🐛 Pass abort signal to Sharp toBuffer for timeout cancellation

Wrap Sharp's toBuffer() with Promise.race to check abort signal during
processing. This ensures large thumbnails stop processing when the
request times out, preventing wasted CPU/memory and queue capacity.

Add test to verify abort during toBuffer operation.

AI-assisted-by: qwen3.7-plus

* 🐛 Hold queue slot until Sharp completes and handle client disconnect

- Remove Promise.race from generateThumbnail — Sharp processing now
  completes fully before queue slot is released, preventing concurrency
  limit violations under timeout conditions
- Remove res.on("finish", release) fallback from queue middleware —
  error handler now explicitly calls releaseQueue in all error paths
- Add res.on("close") handler in timeout middleware to abort signal
  when client disconnects, ensuring processing stops early
- Add tests for client disconnect handling and queue slot lifecycle

AI-assisted-by: qwen3.7-plus

* 🐛 Address round 9 review findings

- Document Sharp 0.35.3 cancellation limitation in image.ts
- Add integration test for timeout cleanup with large images
- Fix font tools (sfntToWoff, woffToSfnt, woff2ToSfnt) to throw
  ProcessingError on resource limit kills instead of returning null
- Validate font signatures for same-format conversions to prevent
  arbitrary files from being persisted as valid fonts
- Fix concurrent mkdtemp race in upload-storage by using shared
  initialization promise

AI-assisted-by: qwen3.7-plus

* 🐛 Address round 10 review findings

- Add tmpdir assertion in font.ts to prevent path injection
- Preserve original error in queue middleware catch handler
- Change auth middleware response type from "internal" to "authorization"
- Add cleanup flag to prevent double cleanup in cleanup middleware
- Move quality clamping into parseQuality function for consistency
- Add integration tests for quality parameter clamping at route level
- Update existing tests to match new auth response type

AI-assisted-by: qwen3.7-plus

* 🐛 Address round 11 review findings

- Extract releaseSlot helper in error-handler to reduce duplication
- Remove redundant try/catch in font.ts withTempDir cleanup
- Improve font path validation error message for clarity
- Move path validation before try/catch to prevent swallowing
- Add debug logging for cleanup failures in cleanup middleware
- Inline TransportTargetSpec type alias in logger.ts
- Extract logging middleware to separate file for consistency
- Remove duplicate MIME validation in image thumbnail route
- Add test for font path validation (outside tmpdir rejection)
- Add tests for error handler queue release across all branches

AI-assisted-by: qwen3.7-plus

* 🐛 Remove Content-Length header from multipart requests

The JDK's HttpClient rejects Content-Length as a restricted header,
causing IllegalArgumentException when sending multipart requests to the
media-processor. Remove the explicit Content-Length header and let the
JDK use chunked transfer encoding. The media-processor will use disk
storage for all multipart requests (safe default behavior).

Remove unused size computations (file-size, header-bytes, footer-bytes,
total-size) that were only used for Content-Length.

Update test to verify Content-Length is not present in request headers.

AI-assisted-by: qwen3.7-plus

* 🐛 Fix pino ESM bundling for media-processor

Mark pino and its transports (pino-pretty, pino-loki) as external to
avoid bundling issues with worker thread modules that reference
__dirname (not available in ES modules).

AI-assisted-by: qwen3.7-plus
2026-08-05 09:41:48 +02:00
Andrey Antukh
3e59754a25 Add optional max-size param to blob decode functions
Accept an optional :max-size keyword argument in blob/decode and
blob/decode-str. When provided, the uncompressed size declared in the
blob header is validated before allocating memory, raising an error if
it exceeds the limit. Callers that do not pass :max-size are unaffected.

AI-assisted-by: deepseek-v4-pro
2026-08-05 08:38:55 +02:00
David Barragán Merino
c16b7919f9 🐳 Remove the configuration of the admin-console from Nginx if it is not enabled 2026-08-04 20:33:31 +02:00
David Barragán Merino
34702fd46b 🐳 Remove the configuration of the admin-console from Nginx if it is not enabled 2026-08-04 20:32:17 +02:00
Andrey Antukh
83a3d099f6 🐛 Mock RPC and timer in composable test interpreter to fix network errors
The `SyncFromLibrary` op dispatches a real `sync-file` event that
schedules `rx/timer 3000` + an RPC call to
`update-file-library-sync-status`. In the headless test runner
(no backend), this produces a network error that leaks into test
output.

Wrap the `check` function in `mock/with-mocks` to mock `rp/cmd!`
(returning success) and `rx/timer` (firing instantly). This
eliminates the 3200ms grace period in `op-grace-ms` and prevents
the network error from appearing in test output.

AI-assisted-by: mimo-v2.5
2026-08-04 15:55:47 +00:00
Andrés Moya
8e713df5f0
🎉 Add repair functions for variant validation errors (#10768)
* 🎉 Add repair functions for variant validation errors

* 📚 Fix copyright notice
2026-08-04 16:53:36 +02:00
Filip Sajdak
3fba272848 🐛 Keep svg-raw children as uuids on binfile import (#10837)
Importing a .penpot file left every svg-raw subtree broken: the parent's
:shapes vector came back holding plain strings instead of uuids, so the
child ids no longer resolved against the page objects map. The next
persisted change touching that page then failed referential integrity
validation with :child-not-found, surfaced to the client as an HTTP 400
:referential-integrity error, which in practice bricks the file.

An svg-raw shape can be a container: importing an SVG builds a tree of
svg-raw shapes, and cfh/group-like-shape? explicitly treats an svg-raw
with children as group-like. But schema:svg-raw-attrs was an empty map.
Frame, group and bool all declare :shapes as a vector of uuid; svg-raw
did not, so the JSON decoder used by binfile had no type information for
those ids and left them as strings.

Declare :shapes on schema:svg-raw-attrs, optional because a leaf svg-raw
shape has no children, so the child ids decode back to uuids.
Closes #10496.

Signed-off-by: Filip Sajdak <filip.sajdak@siili.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-08-04 16:49:33 +02:00
Filip Sajdak
648c8e2152
🐛 Keep svg-raw children as uuids on binfile import (#10837)
Importing a .penpot file left every svg-raw subtree broken: the parent's
:shapes vector came back holding plain strings instead of uuids, so the
child ids no longer resolved against the page objects map. The next
persisted change touching that page then failed referential integrity
validation with :child-not-found, surfaced to the client as an HTTP 400
:referential-integrity error, which in practice bricks the file.

An svg-raw shape can be a container: importing an SVG builds a tree of
svg-raw shapes, and cfh/group-like-shape? explicitly treats an svg-raw
with children as group-like. But schema:svg-raw-attrs was an empty map.
Frame, group and bool all declare :shapes as a vector of uuid; svg-raw
did not, so the JSON decoder used by binfile had no type information for
those ids and left them as strings.

Declare :shapes on schema:svg-raw-attrs, optional because a leaf svg-raw
shape has no children, so the child ids decode back to uuids.
Closes #10496.

Signed-off-by: Filip Sajdak <filip.sajdak@siili.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-08-04 16:49:12 +02:00
Filip Sajdak
7ae57a035f
🐛 Position overlays by frame selrect, not filter-inflated bounds (#10454)
calc-overlay-position measured the destination overlay frame with its full
object bounds (get-object-bounds) while measuring the relative-to frame with
its selrect. Object bounds include padding for shadows, blur, outer strokes
and overflowing children, so centered/right/bottom overlays were shifted by
half that extra padding when the overlay frame had such effects (the overlay
appeared offset, e.g. a bit to the left).

Use the destination frame selrect (the visible frame box) instead, which
matches the sibling helper calc-overlay-pos-initial and the viewer, which
reserves the bounds size and re-aligns the selrect separately. The now unused
geom.shapes.bounds require is removed.

Adds a regression test asserting calc-overlay-position returns the same
position with and without a bounds-inflating drop shadow on the destination
frame.

Fixes #9048

Signed-off-by: Filip Sajdak <filip.sajdak@siili.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-08-04 16:42:55 +02:00
Andrey Antukh
23ea2bbad6 📎 Update creating-commits serena workflow file 2026-08-04 15:36:54 +02:00
Elena Torró
14a6ea5c52
🔧 Support text style shortcuts (#11002) 2026-08-04 15:30:26 +02:00
Andrey Antukh
ca29f734c7 Merge remote-tracking branch 'origin/staging' into develop 2026-08-04 15:08:55 +02:00
Andrey Antukh
b507a6b667 ♻️ Convert commiter agent to create-commit skill
Replace the commiter subagent with a create-commit skill,
consistent with the create-pr and create-issue skill patterns.

- Remove .opencode/agents/commiter.md
- Add .opencode/skills/create-commit/SKILL.md
- Update implement-plan.md to use the skill instead of
  subagent delegation
- Document commit body line wrapping at 72 chars in
  creating-commits memory and skill

AI-assisted-by: mimo-v2.5
2026-08-04 13:08:22 +00:00
Andrey Antukh
3865e29b65 ⬆️ Update pnpm dependencies across all modules 2026-08-04 14:15:35 +02:00
Luis de Dios
43e05c38bf
🐛 Fix error when clicking 'Start' button to finish onboarding after creating a new account (#11058) 2026-08-04 12:44:25 +02:00
David Barragán Merino
0811b1cda6 🔧 Generate the Docker image for the admin console by creating a tag 2026-08-04 11:59:45 +02:00
María Valderrama
edbe9f8215
🐛 Fix nitrate related literals (#11047) 2026-08-04 11:55:01 +02:00
Andrey Antukh
6e843faba3 Merge remote-tracking branch 'origin/staging' into develop 2026-08-03 18:36:27 +02:00
Andrey Antukh
319a2185c9
🐛 Fix crash on drag-and-drop of selected text in Draft.js editor (#10959)
After `Modifier.removeRange` modified the block map, the `targetRange` from
the drop event still referenced stale block keys from the pre-removal DOM
state, causing `TypeError: Cannot read properties of undefined`.

- Added a guarded `moveText` export in `frontend/packages/draft-js/index.js`
  that validates block keys exist before and after removal. Falls back
  gracefully when target range references stale keys.
- Added a `handle-drop` callback in
  `frontend/src/app/main/ui/workspace/shapes/text/editor.cljs` that returns
  "handled" for internal drag operations, preventing Draft.js from calling its
  default (crash-prone) handler.

AI-assisted-by: mimo-v2.5
2026-08-03 17:31:55 +02:00
Andrey Antukh
1136e5eda5
🐛 Restrict update-profile-props to documented keys only (#10992)
Close the profile props schema to reject undocumented keys and add a
denylist for system-managed props like :subscription that should not be
user-writable via RPC.

Changes:
- Add system-managed-props denylist (#{:subscription})
- Close schema:props with :closed true
- Add tests for subscription rejection and valid key acceptance

AI-assisted-by: qwen3.7-plus
2026-08-03 17:27:09 +02:00
Andrey Antukh
49119e0339
♻️ Rename nitrate config to admin-console (#10929)
* ♻️ Rename nitrate config to admin-console

Rename user-facing configuration from 'nitrate' to 'admin-console':
- Feature flags: :nitrate -> :admin-console, :nitrate-bulk-create-profiles -> :admin-console-bulk-create-profiles
- Config keys: :nitrate-shared-key -> :admin-console-shared-key, :nitrate-backend-uri -> :admin-console-uri
- Shared-keys map entry: :nitrate -> :admin-console (setup.clj + main.clj)
- Env vars: PENPOT_NITRATE_SHARED_KEY -> PENPOT_ADMIN_CONSOLE_SHARED_KEY, PENPOT_NITRATE_BACKEND_URI removed (consolidated into PENPOT_ADMIN_CONSOLE_URI)
- Docker/nginx: PENPOT_NITRATE_URI -> PENPOT_ADMIN_CONSOLE_URI

Code namespaces, file paths, CSS classes, and i18n keys stay as-is.

AI-assisted-by: mimo-v2.5-pro

* ♻️ Rename initialize-user-in-nitrate-organization to initialize-user-in-organization

Part of the nitrate -> admin-console rename series. The function and all 9 references across 6 files have been renamed.

* ♻️ Rename :nitrate-bulk-create-profiles-not-allowed to :bulk-create-profiles-not-allowed

* ♻️ Inline nitrate-permissions into app.common.types.organization

- Delete app.common.types.nitrate-permissions and its test
- Move permission rules (allowed?, can-send-invitations?, etc.) into organization.cljc
- Harmonize all consumers to use alias cto for app.common.types.organization
- Update test runner and create organization_test.cljc
2026-08-03 17:23:55 +02:00
Belén Albeza
c6c8a38544
🐛 Fix not being able to add multiple fills to text spans (v3) (#10988) 2026-08-03 17:11:59 +02:00
Elena Torró
0fed63eeb3
🐛 Fix text replacement on selection and text offsets (#10983)
* 🐛 Fix text not being replaced when there is a selection

* 🐛 Fix text editor offsets on transformed text
2026-08-03 15:22:49 +02:00
Andrey Antukh
79da4d274d 📎 Fix linter issues 2026-08-03 14:40:36 +02:00
Andrey Antukh
2f535c3f3f 🐛 Prevent unexpected exception in swatch* component 2026-08-03 14:25:56 +02:00
Andrey Antukh
c320cecf15 📎 Add better prompt for review opencode command 2026-08-03 14:25:19 +02:00
Luis de Dios
141cf7f79f
🐛 Fix main menu does not keep alignment when left sidebar is expanded (#10986) 2026-08-03 10:18:06 +02:00
Luis de Dios
767f90282c
🐛 Fix shape size badge is displayed twice when user with viewer permissions selects a shape (#10985) 2026-08-03 10:16:01 +02:00
Andrey Antukh
eacdca3c0a Merge remote-tracking branch 'origin/develop' into staging 2.18.0-RC1 2026-08-03 09:16:26 +02:00
Andrey Antukh
b72536d312 Merge remote-tracking branch 'origin/staging' into develop 2026-08-03 09:16:09 +02:00
Andrey Antukh
d835baefec Merge remote-tracking branch 'origin/staging' 2.17.1-RC5 2026-08-03 09:15:03 +02:00
Andrey Antukh
f023c09018 Add testing skill with TDD workflow enforcement
Minimal skill referencing mem:testing for full guidance, covering
TDD Red-Green-Refactor cycle, Prove-It pattern for bug fixes,
and key testing principles.

AI-assisted-by: mimo-v2.5-pro
2026-08-01 11:32:14 +00:00
Andrey Antukh
32520e66e5 Allow stopping ws0 independently of other workspaces
Remove the guard in stop-devenv that refused to stop ws0 while any
ws1+ instance was running. Each workspace is now fully independent
and can be started/stopped in any order. Shared infra shuts down
only when no instances remain running.

Updated docs (devenv.md, agentic-devenv.md) and devenv memory to
reflect the new behavior.

AI-assisted-by: mimo-v2.5-pro
2026-08-01 11:29:42 +00:00
Andrey Antukh
5ad0ff752e 📎 Add minor changes on backend package.json 2026-08-01 11:20:31 +02:00
David Barragán Merino
3fc5360df5 🔧 Optimize the bundle and docker images build process 2026-07-31 20:39:51 +02:00
Andrey Antukh
b907bc4cd3 📚 Add hard rule for test output handling to serena memories
Never pipe test output directly to filters (head, tail, grep).
Always redirect to a file first to prevent hiding test failures.

AI-assisted-by: mimo-v2.5
2026-07-31 19:45:06 +02:00
Andrey Antukh
73519778ae 📎 Update serena memories 2026-07-31 19:31:45 +02:00
David Barragán Merino
97a833f1e7
🐳 Migrate penpot images to DHI (#10734)
Move Dockerfile.frontend, .backend, .exporter, .mcp and .storybook
under docker/images/ from ubuntu:26.04 / nginx-unprivileged / a
manual Node tarball install to Docker Hardened Images (Debian 13,
or Alpine for storybook). storybook and mcp get a true non-dev
runtime; frontend, backend and exporter keep the -dev tag as their
final image, since each needs a shell and/or package manager at
container runtime (nginx templating, fontforge/python3, and a
headless-browser stack, respectively).
2026-07-31 19:27:15 +02:00
Andrey Antukh
e741313add Merge remote-tracking branch 'origin/staging' into develop 2026-07-31 19:21:07 +02:00
Andrey Antukh
896aef8c1b 📎 Update imagemagick version on manage.sh 2026-07-31 19:04:33 +02:00
David Barragán Merino
328fa0559a 🐳 Migrate imagemagick and devenv images to DHI
Move docker/imagemagick/Dockerfile and docker/devenv/Dockerfile from
ubuntu:26.04 to Docker Hardened Images (Debian 13 / trixie).
imagemagick gets a true non-dev runtime with its shared libraries
vendored via ldd; devenv keeps the -dev tag as its final image since
it's an interactive development container, not a production
artifact.
2026-07-31 19:04:33 +02:00
Anonymous
863671135d
🌐 Add translations for: French (Canada)
Currently translated at 95.7% (2329 of 2432 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-07-31 19:03:01 +02:00
Alexis Morin
657b5bd94e
🌐 Add translations for: French (Canada)
Currently translated at 95.7% (2329 of 2432 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-07-31 19:03:00 +02:00
Anonymous
5ea3a2efb8
🌐 Add translations for: Hindi
Currently translated at 81.1% (1974 of 2432 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/
2026-07-31 19:02:58 +02:00
VKing9
94cfd6e60d
🌐 Add translations for: Hindi
Currently translated at 81.1% (1974 of 2432 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/
2026-07-31 19:02:58 +02:00