Merge remote-tracking branch 'origin/staging' into develop

This commit is contained in:
Alejandro Alonso 2026-03-25 09:38:06 +01:00
commit caa25c70fc
13 changed files with 368 additions and 184 deletions

View File

@ -1,211 +1,195 @@
# Contributing Guide # # Contributing Guide
Thank you for your interest in contributing to Penpot. This is a Thank you for your interest in contributing to Penpot. This guide covers
generic guide that details how to contribute to the project in a way that how to propose changes, submit fixes, and follow project conventions.
is efficient for everyone. If you are looking for specific documentation on
different parts of the platform, please refer to the `docs/` directory,
or the rendered version at the [Help Center](https://help.penpot.app/).
## Reporting Bugs ## For architecture details, module-specific guidelines, and AI-agent
instructions, see [AGENTS.md](AGENTS.md). For final user technical
documentation, see the `docs/` directory or the rendered [Help
Center](https://help.penpot.app/).
We are using [GitHub Issues](https://github.com/penpot/penpot/issues) ## Table of Contents
for our public bugs. We keep a close eye on them and try to make it
clear when we have an internal fix in progress. Before filing a new
task, try to make sure your problem doesn't already exist.
If you found a bug, please report it, as far as possible, with: - [Prerequisites](#prerequisites)
- [Reporting Bugs](#reporting-bugs)
- [Pull Requests](#pull-requests)
- [Commit Guidelines](#commit-guidelines)
- [Formatting and Linting](#formatting-and-linting)
- [Changelog](#changelog)
- [Code of Conduct](#code-of-conduct)
- [Developer's Certificate of Origin (DCO)](#developers-certificate-of-origin-dco)
- a detailed explanation of steps to reproduce the error ## Prerequisites
- the browser and browser version used
- a dev tools console exception stack trace (if available)
If you found a bug which you think is better to discuss in private (for - **Language**: Penpot is written primarily in Clojure (backend), ClojureScript
example, security bugs), consider first sending an email to (frontend/exporter), and Rust (render-wasm). Familiarity with the Clojure
`support@penpot.app`. ecosystem is expected for most contributions.
- **Issue tracker**: We use [GitHub Issues](https://github.com/penpot/penpot/issues)
for public bugs and [Taiga](https://tree.taiga.io/project/penpot/) for
internal project management. Changelog entries reference both.
**We don't have a formal bug bounty program for security reports; this ## Reporting Bugs
is an open source application, and your contribution will be recognized
in the changelog.**
Report bugs via [GitHub Issues](https://github.com/penpot/penpot/issues).
Before filing, search existing issues to avoid duplicates.
## Pull Requests ## Include the following when possible:
If you want to propose a change or bug fix via a pull request (PR), 1. Steps to reproduce the error.
you should first carefully read the section **Developer's Certificate of 2. Browser and browser version used.
Origin**. You must also format your code and commits according to the 3. DevTools console exception stack trace (if available).
instructions below.
If you intend to fix a bug, it's fine to submit a pull request right For security bugs or issues better discussed in private, email
away, but we still recommend filing an issue detailing what you're `support@penpot.app` or report them on [Github Security
fixing. This is helpful in case we don't accept that specific fix but Advisories](https://github.com/penpot/penpot/security/advisories)
want to keep track of the issue.
If you want to implement or start working on a new feature, please > **Note:** We do not have a formal bug bounty program. Security
open a **question*- / **discussion*- issue for it. No PR > contributions are recognized in the changelog.
will be accepted without a prior discussion about the changes,
whether it is a new feature, an already planned one, or a quick win.
If it is your first PR, you can learn how to proceed from ## Pull Requests
[this free video
series](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github)
We use the `easy fix` tag to indicate issues that are appropriate for beginners. ### Workflow
## Commit Guidelines ## 1. **Read the DCO** — see [Developer's Certificate of Origin](#developers-certificate-of-origin-dco)
below. All code patches must include a `Signed-off-by` line.
2. **Discuss before building** — open a question/discussion issue before
starting work on a new feature or significant change. No PR will be
accepted without prior discussion, whether it is a new feature, a planned
one, or a quick win.
3. **Bug fixes** — you may submit a PR directly, but we still recommend
filing an issue first so we can track it independently of your fix.
4. **Format and lint** — run the checks described in
[Formatting and Linting](#formatting-and-linting) before submitting.
We have very precise rules on how our git commit messages must be formatted. ### Good first issues
The commit message format is: We use the `easy fix` label to mark issues appropriate for newcomers.
## Commit Guidelines
Commit messages must follow this format:
``` ```
<type> <subject> :emoji: <subject>
[body] [body]
[footer] [footer]
``` ```
Where type is: ### Commit types
- :bug: `:bug:` a commit that fixes a bug | Emoji | Description |
- :sparkles: `:sparkles:` a commit that adds an improvement |-------|-------------|
- :tada: `:tada:` a commit with a new feature | :bug: | Bug fix |
- :recycle: `:recycle:` a commit that introduces a refactor | :sparkles: | Improvement or enhancement |
- :lipstick: `:lipstick:` a commit with cosmetic changes | :tada: | New feature |
- :ambulance: `:ambulance:` a commit that fixes a critical bug | :recycle: | Refactor |
- :books: `:books:` a commit that improves or adds documentation | :lipstick: | Cosmetic changes |
- :construction: `:construction:` a WIP commit | :ambulance: | Critical bug fix |
- :boom: `:boom:` a commit with breaking changes | :books: | Documentation |
- :wrench: `:wrench:` a commit for config updates | :construction: | Work in progress |
- :zap: `:zap:` a commit with performance improvements | :boom: | Breaking change |
- :whale: `:whale:` a commit for Docker-related stuff | :wrench: | Configuration update |
- :paperclip: `:paperclip:` a commit with other non-relevant changes | :zap: | Performance improvement |
- :arrow_up: `:arrow_up:` a commit with dependency updates | :whale: | Docker-related change |
- :arrow_down: `:arrow_down:` a commit with dependency downgrades | :paperclip: | Other non-relevant changes |
- :fire: `:fire:` a commit that removes files or code | :arrow_up: | Dependency update |
- :globe_with_meridians: `:globe_with_meridians:` a commit that adds or updates | :arrow_down: | Dependency downgrade |
translations | :fire: | Removal of code or files |
| :globe_with_meridians: | Add or update translations |
| :rocket: | Epic or highlight |
More info: ### Rules
- https://gist.github.com/parmentf/035de27d6ed1dce0b36a - Use the **imperative mood** in the subject (e.g. "Fix", not "Fixed").
- https://gist.github.com/rxaviers/7360908 - Capitalize the first letter of the subject.
- Do not end the subject with a period.
- Keep the subject to **65 characters** or fewer.
- Separate the subject from the body with a **blank line**.
Each commit should have: ### Examples
- A concise subject using the imperative mood. ```
- The subject should capitalize the first letter, omit the period :bug: Fix unexpected error on launching modal
at the end, and be no longer than 65 characters. :sparkles: Enable new modal for profile
- A blank line between the subject line and the body. :zap: Improve performance of dashboard navigation
- An entry in the CHANGES.md file if applicable, referencing the :ambulance: Fix critical bug on user registration process
GitHub or Taiga issue/user story using these same rules. :tada: Add new approach for user registration
```
Examples of good commit messages: ## Formatting and Linting
- `:bug: Fix unexpected error on launching modal` We use [cljfmt](https://github.com/weavejester/cljfmt) for formatting and
- `:bug: Set proper error message on generic error` [clj-kondo](https://github.com/clj-kondo/clj-kondo) for linting.
- `:sparkles: Enable new modal for profile`
- `:zap: Improve performance of dashboard navigation`
- `:wrench: Update default backend configuration`
- `:books: Add more documentation for authentication process`
- `:ambulance: Fix critical bug on user registration process`
- `:tada: Add new approach for user registration`
## Formatting and Linting ##
You will want to make sure your code is formatted and linted before submitting
a PR. We use [cljfmt](https://github.com/weavejester/cljfmt) and
[clj-kondo](https://github.com/clj-kondo/clj-kondo) for this. After installing
them on your system, you can run them with:
```bash ```bash
# Check formatting # Check formatting (does not modify files)
./scripts/check-fmt
# Fix formatting (modifies files in place)
./scripts/fmt ./scripts/fmt
# Lint # Lint
./scripts/lint ./scripts/lint
``` ```
Ideally, you should run these commands as git pre-commit hooks. A convenient way Ideally, run these as git pre-commit hooks.
of defining them is to use [Husky](https://typicode.github.io/husky/#/). [Husky](https://typicode.github.io/husky/#/) is a convenient option for
setting this up.
## Code of Conduct ## ## Changelog
As contributors and maintainers of this project, we pledge to respect When your change is user-facing or otherwise notable, add an entry to
all people who contribute through reporting issues, posting feature [CHANGES.md](CHANGES.md) following the same commit-type conventions. Reference
requests, updating documentation, submitting pull requests or patches, the relevant GitHub issue or Taiga user story.
and other activities.
We are committed to making participation in this project a ## Code of Conduct
harassment-free experience for everyone, regardless of level of
experience, gender, gender identity and expression, sexual
orientation, disability, personal appearance, body size, race,
ethnicity, age, or religion.
Examples of unacceptable behavior by participants include the use of This project follows the [Contributor Covenant](https://www.contributor-covenant.org/).
sexual language or imagery, derogatory comments or personal attacks, The full Code of Conduct is available at
trolling, public or private harassment, insults, or other [help.penpot.app/contributing-guide/coc](https://help.penpot.app/contributing-guide/coc/)
unprofessional conduct. and in the repository's [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
Project maintainers have the right and responsibility to remove, edit, To report unacceptable behavior, open an issue or contact a project maintainer
or reject comments, commits, code, wiki edits, issues, and other directly.
contributions that are not aligned with this Code of Conduct. Project
maintainers who do not follow the Code of Conduct may be removed from
the project team.
This Code of Conduct applies both within project spaces and in public
spaces when an individual is representing the project or its
community.
Instances of abusive, harassing, or otherwise unacceptable behavior
may be reported by opening an issue or contacting one or more of the
project maintainers.
This Code of Conduct is adapted from the Contributor Covenant, version
1.1.0, available from [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/)
## Developer's Certificate of Origin (DCO) ## Developer's Certificate of Origin (DCO)
By submitting code you agree to and can certify the following: By submitting code you agree to and can certify the following:
Developer's Certificate of Origin 1.1 > **Developer's Certificate of Origin 1.1**
>
> By making a contribution to this project, I certify that:
>
> (a) The contribution was created in whole or in part by me and I have the
> right to submit it under the open source license indicated in the file; or
>
> (b) The contribution is based upon previous work that, to the best of my
> knowledge, is covered under an appropriate open source license and I have
> the right under that license to submit that work with modifications,
> whether created in whole or in part by me, under the same open source
> license (unless I am permitted to submit under a different license), as
> indicated in the file; or
>
> (c) The contribution was provided directly to me by some other person who
> certified (a), (b) or (c) and I have not modified it.
>
> (d) I understand and agree that this project and the contribution are public
> and that a record of the contribution (including all personal information
> I submit with it, including my sign-off) is maintained indefinitely and
> may be redistributed consistent with this project or the open source
> license(s) involved.
By making a contribution to this project, I certify that: ### Signed-off-by
(a) The contribution was created in whole or in part by me and I All code patches (**documentation is excluded**) must contain a sign-off line
have the right to submit it under the open source license at the end of the commit body. Add it automatically with `git commit -s`.
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
Then, all your code patches (**documentation is excluded**) should
contain a sign-off at the end of the patch/commit description body. It
can be automatically added by adding the `-s` parameter to `git commit`.
This is an example of what the line should look like:
``` ```
Signed-off-by: Andrey Antukh <niwi@niwi.nz> Signed-off-by: Your Real Name <your.email@example.com>
``` ```
Please, use your real name (sorry, no pseudonyms or anonymous - Use your **real name** — pseudonyms and anonymous contributions are not
contributions are allowed). allowed.
- The `Signed-off-by` line is **mandatory** and must match the commit author.
The commit Signed-off-by is mandatory and should match the commit author.

View File

@ -245,8 +245,10 @@
(defn format-throwable (defn format-throwable
[cause & {:as opts}] [cause & {:as opts}]
(with-out-str (with-out-str
(println "====================")
(when-let [exdata (ex-data cause)] (when-let [exdata (ex-data cause)]
(when-let [hint (get exdata :hint)] (when-let [hint (or (get exdata :hint)
(ex-message cause))]
(when (str/index-of hint "\n") (when (str/index-of hint "\n")
(println "Hint:") (println "Hint:")
(println "--------------------") (println "--------------------")
@ -273,7 +275,9 @@
(when-let [trace (.-stack cause)] (when-let [trace (.-stack cause)]
(println "Trace:") (println "Trace:")
(println "--------------------") (println "--------------------")
(println (.-stack cause)))))) (println (.-stack cause)))
(println "===================="))))
(defn first-line (defn first-line
[s] [s]
@ -297,6 +301,11 @@
(js/console.group title) (js/console.group title)
(try (try
(js/console.log (format-throwable cause)) (js/console.log (format-throwable cause))
(loop [cause (ex-cause cause)]
(when cause
(js/console.log "\nCaused by:")
(js/console.log (format-throwable cause))
(recur (ex-cause cause))))
(finally (finally
(js/console.groupEnd)))))) (js/console.groupEnd))))))

View File

@ -0,0 +1,19 @@
{
"~:file-id": "~u7fd33337-c651-80ae-8007-c357213f876e",
"~:id": "~u7fd33337-c651-80ae-8007-c3578977e5be",
"~:created-at": "~m1774363460068",
"~:modified-at": "~m1774363460068",
"~:type": "fragment",
"~:backend": "db",
"~:data": {
"~:objects": {
"~#penpot/objects-map/v2": {
"~u00000000-0000-0000-0000-000000000000": "[\"~#shape\",[\"^ \",\"~:y\",0,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:name\",\"Root Frame\",\"~:width\",0.01,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",0.0,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.01]],[\"^:\",[\"^ \",\"~:x\",0.0,\"~:y\",0.01]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:r3\",0,\"~:r1\",0,\"~:id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",0,\"~:proportion\",1.0,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",0,\"~:y\",0,\"^6\",0.01,\"~:height\",0.01,\"~:x1\",0,\"~:y1\",0,\"~:x2\",0.01,\"~:y2\",0.01]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#FFFFFF\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^H\",0.01,\"~:flip-y\",null,\"~:shapes\",[\"~ub98e38af-59e9-8056-8007-c3577ef85c83\"]]]",
"~ub98e38af-59e9-8056-8007-c35778509984": "[\"~#shape\",[\"^ \",\"~:y\",522.9999389648438,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:auto-width\",\"~:content\",[\"^ \",\"~:type\",\"root\",\"~:key\",\"lu847h6p2o\",\"~:children\",[[\"^ \",\"^7\",\"paragraph-set\",\"^9\",[[\"^ \",\"~:line-height\",\"1.2\",\"~:font-style\",\"normal\",\"^9\",[[\"^ \",\"^:\",\"1.2\",\"^;\",\"normal\",\"~:typography-ref-id\",null,\"~:text-transform\",\"none\",\"~:font-id\",\"sourcesanspro\",\"^8\",\"20x1m8p51r5\",\"~:font-size\",\"14\",\"~:font-weight\",\"400\",\"~:typography-ref-file\",null,\"~:font-variant-id\",\"regular\",\"~:text-decoration\",\"none\",\"~:letter-spacing\",\"0\",\"~:fills\",[[\"^ \",\"~:fill-color\",\"#000000\",\"~:fill-opacity\",1]],\"~:font-family\",\"sourcesanspro\",\"~:text\",\"uno dos tres cuatro\"]],\"^<\",null,\"^=\",\"none\",\"~:text-align\",\"left\",\"^>\",\"sourcesanspro\",\"^8\",\"5tp2r0veqv\",\"^?\",\"14\",\"^@\",\"400\",\"^A\",null,\"~:text-direction\",\"ltr\",\"^7\",\"paragraph\",\"^B\",\"regular\",\"^C\",\"none\",\"^D\",\"0\",\"^E\",[[\"^ \",\"^F\",\"#000000\",\"^G\",1]],\"^H\",\"sourcesanspro\"]]]],\"~:vertical-align\",\"top\"],\"~:hide-in-viewer\",false,\"~:name\",\"uno dos tres cuatro\",\"~:width\",113,\"^7\",\"^I\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",861,\"~:y\",522.9999389648438]],[\"^Q\",[\"^ \",\"~:x\",974,\"~:y\",522.9999389648438]],[\"^Q\",[\"^ \",\"~:x\",974,\"~:y\",539.9999389648438]],[\"^Q\",[\"^ \",\"~:x\",861,\"~:y\",539.9999389648438]]],\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:id\",\"~ub98e38af-59e9-8056-8007-c35778509984\",\"~:parent-id\",\"~ub98e38af-59e9-8056-8007-c3577ef85c83\",\"~:position-data\",[[\"^ \",\"~:y\",540.659912109375,\"^:\",\"1.2\",\"^;\",\"normal\",\"^=\",\"none\",\"^J\",\"left\",\"^>\",\"sourcesanspro\",\"^?\",\"14\",\"^@\",\"400\",\"^K\",\"ltr\",\"^O\",112.989990234375,\"^B\",\"regular\",\"^C\",\"none\",\"^D\",\"0\",\"~:x\",861,\"^E\",[[\"^ \",\"^F\",\"#000000\",\"^G\",1]],\"~:direction\",\"ltr\",\"^H\",\"sourcesanspro\",\"~:height\",18.1199951171875,\"^I\",\"uno dos tres cuatro\"]],\"~:frame-id\",\"~ub98e38af-59e9-8056-8007-c3577ef85c83\",\"~:x\",861,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",861,\"~:y\",522.9999389648438,\"^O\",113,\"^W\",17,\"~:x1\",861,\"~:y1\",522.9999389648438,\"~:x2\",974,\"~:y2\",539.9999389648438]],\"~:flip-x\",null,\"^W\",17,\"~:flip-y\",null]]",
"~ub98e38af-59e9-8056-8007-c3577ef85c83": "[\"~#shape\",[\"^ \",\"~:y\",512.9999568462372,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",10,\"~:p2\",10,\"~:p3\",10,\"~:p4\",10],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"Board\",\"~:layout-align-items\",\"~:start\",\"~:width\",132.99999487400055,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",850.9999846220016,\"~:y\",512.9999568462372]],[\"^J\",[\"^ \",\"~:x\",983.9999794960022,\"~:y\",512.9999568462372]],[\"^J\",[\"^ \",\"~:x\",983.9999794960022,\"~:y\",549.9999556541443]],[\"^J\",[\"^ \",\"~:x\",850.9999846220016,\"~:y\",549.9999556541443]]],\"~:r2\",0,\"~:show-content\",true,\"~:layout-item-h-sizing\",\"~:auto\",\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",0,\"~:column-gap\",0],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:layout-item-v-sizing\",\"^N\",\"~:r3\",0,\"~:layout-justify-content\",\"^C\",\"~:r1\",0,\"~:id\",\"~ub98e38af-59e9-8056-8007-c3577ef85c83\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:layout-flex-dir\",\"~:row\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",850.9999846220016,\"~:proportion\",1,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",850.9999846220016,\"~:y\",512.9999568462372,\"^D\",132.99999487400055,\"~:height\",36.999998807907104,\"~:x1\",850.9999846220016,\"~:y1\",512.9999568462372,\"~:x2\",983.9999794960022,\"~:y2\",549.9999556541443]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#ffffff\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^18\",36.999998807907104,\"~:flip-y\",null,\"~:shapes\",[\"~ub98e38af-59e9-8056-8007-c35778509984\"]]]"
}
},
"~:id": "~u7fd33337-c651-80ae-8007-c357213f876f",
"~:name": "Page 1"
}
}

View File

@ -0,0 +1,135 @@
{
"~:features": {
"~#set": [
"fdata/path-data",
"plugins/runtime",
"design-tokens/v1",
"variants/v1",
"layout/grid",
"styles/v2",
"fdata/pointer-map",
"fdata/objects-map",
"render-wasm/v1",
"components/v2",
"fdata/shape-data-type"
]
},
"~:team-id": "~ud715d0a5-a44e-8056-8005-a79999e18b64",
"~:permissions": {
"~:type": "~:membership",
"~:is-owner": true,
"~:is-admin": true,
"~:can-edit": true,
"~:can-read": true,
"~:is-logged": true
},
"~:has-media-trimmed": false,
"~:comment-thread-seqn": 0,
"~:name": "New File 10",
"~:revn": 2,
"~:modified-at": "~m1774363460059",
"~:vern": 0,
"~:id": "~u7fd33337-c651-80ae-8007-c357213f876e",
"~:is-shared": false,
"~:migrations": {
"~#ordered-set": [
"legacy-2",
"legacy-3",
"legacy-5",
"legacy-6",
"legacy-7",
"legacy-8",
"legacy-9",
"legacy-10",
"legacy-11",
"legacy-12",
"legacy-13",
"legacy-14",
"legacy-16",
"legacy-17",
"legacy-18",
"legacy-19",
"legacy-25",
"legacy-26",
"legacy-27",
"legacy-28",
"legacy-29",
"legacy-31",
"legacy-32",
"legacy-33",
"legacy-34",
"legacy-36",
"legacy-37",
"legacy-38",
"legacy-39",
"legacy-40",
"legacy-41",
"legacy-42",
"legacy-43",
"legacy-44",
"legacy-45",
"legacy-46",
"legacy-47",
"legacy-48",
"legacy-49",
"legacy-50",
"legacy-51",
"legacy-52",
"legacy-53",
"legacy-54",
"legacy-55",
"legacy-56",
"legacy-57",
"legacy-59",
"legacy-62",
"legacy-65",
"legacy-66",
"legacy-67",
"0001-remove-tokens-from-groups",
"0002-normalize-bool-content-v2",
"0002-clean-shape-interactions",
"0003-fix-root-shape",
"0003-convert-path-content-v2",
"0005-deprecate-image-type",
"0006-fix-old-texts-fills",
"0008-fix-library-colors-v4",
"0009-clean-library-colors",
"0009-add-partial-text-touched-flags",
"0010-fix-swap-slots-pointing-non-existent-shapes",
"0011-fix-invalid-text-touched-flags",
"0012-fix-position-data",
"0013-fix-component-path",
"0013-clear-invalid-strokes-and-fills",
"0014-fix-tokens-lib-duplicate-ids",
"0014-clear-components-nil-objects",
"0015-fix-text-attrs-blank-strings",
"0015-clean-shadow-color",
"0016-copy-fills-from-position-data-to-text-node",
"0017-fix-layout-flex-dir"
]
},
"~:version": 67,
"~:project-id": "~u76eab896-accf-81a5-8007-2b264ebe7817",
"~:created-at": "~m1774363353342",
"~:backend": "legacy-db",
"~:data": {
"~:pages": [
"~u7fd33337-c651-80ae-8007-c357213f876f"
],
"~:pages-index": {
"~u7fd33337-c651-80ae-8007-c357213f876f": {
"~#penpot/pointer": [
"~u7fd33337-c651-80ae-8007-c3578977e5be",
{
"~:created-at": "~m1774363460064"
}
]
}
},
"~:id": "~u7fd33337-c651-80ae-8007-c357213f876e",
"~:options": {
"~:components-v2": true,
"~:base-font-size": "16px"
}
}
}

View File

@ -112,3 +112,30 @@ test("BUG 13272 - Fix problem with snap to pixel", async ({ page }) => {
await expect(workspacePage.rightSidebar.getByTitle("Width").getByRole("textbox")).toHaveValue("197.5"); await expect(workspacePage.rightSidebar.getByTitle("Width").getByRole("textbox")).toHaveValue("197.5");
await expect(workspacePage.rightSidebar.getByTitle("Height").getByRole("textbox")).toHaveValue("128.28"); await expect(workspacePage.rightSidebar.getByTitle("Height").getByRole("textbox")).toHaveValue("128.28");
}); });
test("BUG 13755 - Fix problem with text change modiifers", async ({ page }) => {
const workspacePage = new WasmWorkspacePage(page);
await workspacePage.setupEmptyFile();
await workspacePage.mockGetFile("workspace/get-file-13755.json");
await workspacePage.mockRPC(
"get-file-fragment?file-id=*&fragment-id=*",
"workspace/get-file-13755-fragment.json",
);
await workspacePage.mockRPC("update-file?id=*", "workspace/update-file-empty.json");
await workspacePage.goToWorkspace({
fileId: "7fd33337-c651-80ae-8007-c357213f876e",
pageId: "7fd33337-c651-80ae-8007-c357213f876f",
});
await workspacePage.clickToggableLayer("Board");
await workspacePage.clickLeafLayer("uno dos tres cuatro");
await workspacePage.page.keyboard.press('Enter');
await workspacePage.page.keyboard.type('test');
await workspacePage.clickToggableLayer("Board");
await expect(workspacePage.rightSidebar.getByTitle("Width").getByRole("textbox")).toHaveValue("23");
});

View File

@ -178,7 +178,7 @@
(rx/map (fn [{:keys [redirect-uri] :as rsp}] (rx/map (fn [{:keys [redirect-uri] :as rsp}]
(if redirect-uri (if redirect-uri
(rt/nav-raw :uri redirect-uri) (rt/nav-raw :uri redirect-uri)
(ex/raise :type :internal (ex/raise :type :assertion
:code :unexpected-response :code :unexpected-response
:hint "unexpected response from OIDC method" :hint "unexpected response from OIDC method"
:resp (pr-str rsp))))) :resp (pr-str rsp)))))

View File

@ -1026,10 +1026,15 @@
:stack-undo? effective-stack-undo? :stack-undo? effective-stack-undo?
:undo-group (when new-shape? id)}) :undo-group (when new-shape? id)})
;; When we don't update the shape (no new-size), still update WASM display ;; When `get-wasm-text-new-size` reports a change, `update-shapes` above resizes the
(when-not (some? new-size) ;; shape data; the WASM renderer still needs matching modifiers. While editing, use
;; `set-wasm-modifiers` for a temporary preview; on `finalize?`, `apply-wasm-modifiers`
;; commits layout (flex parents, sidebar width, etc.) like other transform flows.
(when (some? new-size)
(when-let [modifiers (dwwt/resize-wasm-text-modifiers shape content)] (when-let [modifiers (dwwt/resize-wasm-text-modifiers shape content)]
(dwm/set-wasm-modifiers modifiers {:undo-group (when new-shape? id)})))) (if finalize?
(dwm/apply-wasm-modifiers modifiers {:undo-group (when new-shape? id)})
(dwm/set-wasm-modifiers modifiers {:undo-group (when new-shape? id)})))))
(when finalize? (when finalize?
(rx/concat (rx/concat

View File

@ -139,6 +139,12 @@
:level :error :level :error
:timeout 5000}))) :timeout 5000})))
(defmethod ptk/handle-error :internal
[error]
(st/emit! (rt/assign-exception error))
(when-let [cause (::instance error)]
(ex/print-throwable cause :prefix "Internal Error")))
(defmethod ptk/handle-error :default (defmethod ptk/handle-error :default
[error] [error]
(if (and (string? (:hint error)) (if (and (string? (:hint error))
@ -240,8 +246,7 @@
(st/async-emit! (rt/assign-exception error)))) (st/async-emit! (rt/assign-exception error))))
;; This is a pure frontend error that can be caused by an active ;; This is a pure frontend error that can be caused by an active
;; assertion (assertion that is preserved on production builds). From ;; assertion (assertion that is preserved on production builds).
;; the user perspective this should be treated as internal error.
(defmethod ptk/handle-error :assertion (defmethod ptk/handle-error :assertion
[error] [error]
(when-let [cause (::instance error)] (when-let [cause (::instance error)]

View File

@ -57,13 +57,13 @@
:else :else
(rx/throw (rx/throw
(ex-info "repository request error" (ex/error :type :internal
{:type :internal :code :unable-to-process-repository-response
:code :repository-access-error :hint "unable to process repository response"
:uri uri :uri uri
:status status :status status
:headers headers :headers headers
:data body})))) :data body))))
(def default-options (def default-options
{:update-file {:query-params [:id]} {:update-file {:query-params [:id]}
@ -156,11 +156,11 @@
tpoint (ct/tpoint-ms)] tpoint (ct/tpoint-ms)]
(when (and response-stream? (not stream?)) (when (and response-stream? (not stream?))
(ex/raise :type :internal (ex/raise :type :assertion
:code :invalid-response-processing :code :unexpected-response
:hint "expected normal response, received sse stream" :hint "expected normal response, received sse stream"
:response-uri (:uri response) :uri (:uri response)
:response-status (:status response))) :status (:status response)))
(if response-stream? (if response-stream?
(-> (sse/create-stream body) (-> (sse/create-stream body)

View File

@ -44,8 +44,8 @@
(rx/end! subs))) (rx/end! subs)))
(obj/set! image "crossOrigin" "anonymous") (obj/set! image "crossOrigin" "anonymous")
(obj/set! image "onerror" #(rx/error! subs %)) (obj/set! image "onerror" #(rx/error! subs %))
(obj/set! image "onabort" #(rx/error! subs (ex/error :type :internal (obj/set! image "onabort" #(rx/error! subs (ex/error :type :abort
:code :abort :code :operation-aborted
:hint "operation aborted"))) :hint "operation aborted")))
(obj/set! image "src" uri) (obj/set! image "src" uri)
(fn [] (fn []

View File

@ -106,10 +106,10 @@
(p/catch (p/catch
(fn [cause] (fn [cause]
(vreset! abortable? false) (vreset! abortable? false)
(when-not @unsubscribed? (when-not (or @unsubscribed? (= (.-name ^js cause) "AbortError"))
(let [error (ex-info (ex-message cause) (let [error (ex-info (ex-message cause)
{:type :internal {:type :internal
:code :unable-to-fetch :code :fetch-error
:hint "unable to perform fetch operation" :hint "unable to perform fetch operation"
:uri uri :uri uri
:headers headers} :headers headers}

View File

@ -42,8 +42,8 @@
(obj/set! reader "onerror" (obj/set! reader "onerror"
#(rx/error! subs %)) #(rx/error! subs %))
(obj/set! reader "onabort" (obj/set! reader "onabort"
#(rx/error! subs (ex/error :type :internal #(rx/error! subs (ex/error :type :abort
:code :abort :code :operation-aborted
:hint "operation aborted"))) :hint "operation aborted")))
(f reader) (f reader)
(fn [] (fn []

View File

@ -8,6 +8,7 @@
"Helpers for make zip file." "Helpers for make zip file."
(:require (:require
["@zip.js/zip.js" :as zip] ["@zip.js/zip.js" :as zip]
[app.common.exceptions :as ex]
[app.util.array :as array] [app.util.array :as array]
[promesa.core :as p])) [promesa.core :as p]))
@ -27,9 +28,9 @@
(reader (js/Uint8Array. blob)) (reader (js/Uint8Array. blob))
:else :else
(throw (ex-info "invalid arguments" (ex/raise :type :assertion
{:type :internal :coce :invalid-type
:code :invalid-type})))) :hint "invalid data received for zip/reader")))
(defn blob-writer (defn blob-writer
[& {:keys [mtype]}] [& {:keys [mtype]}]
@ -62,10 +63,9 @@
(.add writer path (new zip/TextReader content)) (.add writer path (new zip/TextReader content))
:else :else
(throw (ex-info "invalid arguments" (ex/raise :type :assertion
{:type :internal :code :invalid-type
:code :invalid-type})))) :hint "invalid data received for zip/add fn")))
(defn get-entry (defn get-entry
[reader path] [reader path]