mirror of
https://github.com/penpot/penpot.git
synced 2026-03-30 08:10:30 +02:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
294
CONTRIBUTING.md
294
CONTRIBUTING.md
@@ -1,211 +1,195 @@
|
||||
# Contributing Guide #
|
||||
# Contributing Guide
|
||||
|
||||
Thank you for your interest in contributing to Penpot. This is a
|
||||
generic guide that details how to contribute to the project in a way that
|
||||
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/).
|
||||
Thank you for your interest in contributing to Penpot. This guide covers
|
||||
how to propose changes, submit fixes, and follow project conventions.
|
||||
|
||||
## 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)
|
||||
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.
|
||||
## Table of Contents
|
||||
|
||||
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
|
||||
- the browser and browser version used
|
||||
- a dev tools console exception stack trace (if available)
|
||||
## Prerequisites
|
||||
|
||||
If you found a bug which you think is better to discuss in private (for
|
||||
example, security bugs), consider first sending an email to
|
||||
`support@penpot.app`.
|
||||
- **Language**: Penpot is written primarily in Clojure (backend), ClojureScript
|
||||
(frontend/exporter), and Rust (render-wasm). Familiarity with the Clojure
|
||||
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
|
||||
is an open source application, and your contribution will be recognized
|
||||
in the changelog.**
|
||||
## Reporting Bugs
|
||||
|
||||
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),
|
||||
you should first carefully read the section **Developer's Certificate of
|
||||
Origin**. You must also format your code and commits according to the
|
||||
instructions below.
|
||||
1. Steps to reproduce the error.
|
||||
2. Browser and browser version used.
|
||||
3. DevTools console exception stack trace (if available).
|
||||
|
||||
If you intend to fix a bug, it's fine to submit a pull request right
|
||||
away, but we still recommend filing an issue detailing what you're
|
||||
fixing. This is helpful in case we don't accept that specific fix but
|
||||
want to keep track of the issue.
|
||||
For security bugs or issues better discussed in private, email
|
||||
`support@penpot.app` or report them on [Github Security
|
||||
Advisories](https://github.com/penpot/penpot/security/advisories)
|
||||
|
||||
If you want to implement or start working on a new feature, please
|
||||
open a **question*- / **discussion*- issue for it. No PR
|
||||
will be accepted without a prior discussion about the changes,
|
||||
whether it is a new feature, an already planned one, or a quick win.
|
||||
> **Note:** We do not have a formal bug bounty program. Security
|
||||
> contributions are recognized in the changelog.
|
||||
|
||||
If it is your first PR, you can learn how to proceed from
|
||||
[this free video
|
||||
series](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github)
|
||||
## Pull Requests
|
||||
|
||||
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]
|
||||
|
||||
[footer]
|
||||
```
|
||||
|
||||
Where type is:
|
||||
### Commit types
|
||||
|
||||
- :bug: `:bug:` a commit that fixes a bug
|
||||
- :sparkles: `:sparkles:` a commit that adds an improvement
|
||||
- :tada: `:tada:` a commit with a new feature
|
||||
- :recycle: `:recycle:` a commit that introduces a refactor
|
||||
- :lipstick: `:lipstick:` a commit with cosmetic changes
|
||||
- :ambulance: `:ambulance:` a commit that fixes a critical bug
|
||||
- :books: `:books:` a commit that improves or adds documentation
|
||||
- :construction: `:construction:` a WIP commit
|
||||
- :boom: `:boom:` a commit with breaking changes
|
||||
- :wrench: `:wrench:` a commit for config updates
|
||||
- :zap: `:zap:` a commit with performance improvements
|
||||
- :whale: `:whale:` a commit for Docker-related stuff
|
||||
- :paperclip: `:paperclip:` a commit with other non-relevant changes
|
||||
- :arrow_up: `:arrow_up:` a commit with dependency updates
|
||||
- :arrow_down: `:arrow_down:` a commit with dependency downgrades
|
||||
- :fire: `:fire:` a commit that removes files or code
|
||||
- :globe_with_meridians: `:globe_with_meridians:` a commit that adds or updates
|
||||
translations
|
||||
| Emoji | Description |
|
||||
|-------|-------------|
|
||||
| :bug: | Bug fix |
|
||||
| :sparkles: | Improvement or enhancement |
|
||||
| :tada: | New feature |
|
||||
| :recycle: | Refactor |
|
||||
| :lipstick: | Cosmetic changes |
|
||||
| :ambulance: | Critical bug fix |
|
||||
| :books: | Documentation |
|
||||
| :construction: | Work in progress |
|
||||
| :boom: | Breaking change |
|
||||
| :wrench: | Configuration update |
|
||||
| :zap: | Performance improvement |
|
||||
| :whale: | Docker-related change |
|
||||
| :paperclip: | Other non-relevant changes |
|
||||
| :arrow_up: | Dependency update |
|
||||
| :arrow_down: | Dependency downgrade |
|
||||
| :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
|
||||
- https://gist.github.com/rxaviers/7360908
|
||||
- Use the **imperative mood** in the subject (e.g. "Fix", not "Fixed").
|
||||
- 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
|
||||
at the end, and be no longer than 65 characters.
|
||||
- A blank line between the subject line and the body.
|
||||
- An entry in the CHANGES.md file if applicable, referencing the
|
||||
GitHub or Taiga issue/user story using these same rules.
|
||||
```
|
||||
:bug: Fix unexpected error on launching modal
|
||||
:sparkles: Enable new modal for profile
|
||||
:zap: Improve performance of dashboard navigation
|
||||
:ambulance: Fix critical bug on user registration process
|
||||
:tada: Add new approach for user registration
|
||||
```
|
||||
|
||||
Examples of good commit messages:
|
||||
## Formatting and Linting
|
||||
|
||||
- `:bug: Fix unexpected error on launching modal`
|
||||
- `:bug: Set proper error message on generic error`
|
||||
- `: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:
|
||||
We use [cljfmt](https://github.com/weavejester/cljfmt) for formatting and
|
||||
[clj-kondo](https://github.com/clj-kondo/clj-kondo) for linting.
|
||||
|
||||
```bash
|
||||
# Check formatting
|
||||
# Check formatting (does not modify files)
|
||||
./scripts/check-fmt
|
||||
|
||||
# Fix formatting (modifies files in place)
|
||||
./scripts/fmt
|
||||
|
||||
# Lint
|
||||
./scripts/lint
|
||||
```
|
||||
|
||||
Ideally, you should run these commands as git pre-commit hooks. A convenient way
|
||||
of defining them is to use [Husky](https://typicode.github.io/husky/#/).
|
||||
Ideally, run these as git pre-commit hooks.
|
||||
[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
|
||||
all people who contribute through reporting issues, posting feature
|
||||
requests, updating documentation, submitting pull requests or patches,
|
||||
and other activities.
|
||||
When your change is user-facing or otherwise notable, add an entry to
|
||||
[CHANGES.md](CHANGES.md) following the same commit-type conventions. Reference
|
||||
the relevant GitHub issue or Taiga user story.
|
||||
|
||||
We are committed to making participation in this project a
|
||||
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.
|
||||
## Code of Conduct
|
||||
|
||||
Examples of unacceptable behavior by participants include the use of
|
||||
sexual language or imagery, derogatory comments or personal attacks,
|
||||
trolling, public or private harassment, insults, or other
|
||||
unprofessional conduct.
|
||||
This project follows the [Contributor Covenant](https://www.contributor-covenant.org/).
|
||||
The full Code of Conduct is available at
|
||||
[help.penpot.app/contributing-guide/coc](https://help.penpot.app/contributing-guide/coc/)
|
||||
and in the repository's [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit,
|
||||
or reject comments, commits, code, wiki edits, issues, and other
|
||||
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/)
|
||||
To report unacceptable behavior, open an issue or contact a project maintainer
|
||||
directly.
|
||||
|
||||
## Developer's Certificate of Origin (DCO)
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
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:
|
||||
All code patches (**documentation is excluded**) must contain a sign-off line
|
||||
at the end of the commit body. Add it automatically with `git commit -s`.
|
||||
|
||||
```
|
||||
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
|
||||
contributions are allowed).
|
||||
|
||||
The commit Signed-off-by is mandatory and should match the commit author.
|
||||
|
||||
- Use your **real name** — pseudonyms and anonymous contributions are not
|
||||
allowed.
|
||||
- The `Signed-off-by` line is **mandatory** and must match the commit author.
|
||||
|
||||
@@ -245,8 +245,10 @@
|
||||
(defn format-throwable
|
||||
[cause & {:as opts}]
|
||||
(with-out-str
|
||||
(println "====================")
|
||||
(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")
|
||||
(println "Hint:")
|
||||
(println "--------------------")
|
||||
@@ -273,7 +275,9 @@
|
||||
(when-let [trace (.-stack cause)]
|
||||
(println "Trace:")
|
||||
(println "--------------------")
|
||||
(println (.-stack cause))))))
|
||||
(println (.-stack cause)))
|
||||
|
||||
(println "===================="))))
|
||||
|
||||
(defn first-line
|
||||
[s]
|
||||
@@ -297,6 +301,11 @@
|
||||
(js/console.group title)
|
||||
(try
|
||||
(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
|
||||
(js/console.groupEnd))))))
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
135
frontend/playwright/data/workspace/get-file-13755.json
Normal file
135
frontend/playwright/data/workspace/get-file-13755.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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("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");
|
||||
});
|
||||
|
||||
@@ -178,7 +178,7 @@
|
||||
(rx/map (fn [{:keys [redirect-uri] :as rsp}]
|
||||
(if redirect-uri
|
||||
(rt/nav-raw :uri redirect-uri)
|
||||
(ex/raise :type :internal
|
||||
(ex/raise :type :assertion
|
||||
:code :unexpected-response
|
||||
:hint "unexpected response from OIDC method"
|
||||
:resp (pr-str rsp)))))
|
||||
|
||||
@@ -1026,10 +1026,15 @@
|
||||
:stack-undo? effective-stack-undo?
|
||||
:undo-group (when new-shape? id)})
|
||||
|
||||
;; When we don't update the shape (no new-size), still update WASM display
|
||||
(when-not (some? new-size)
|
||||
;; When `get-wasm-text-new-size` reports a change, `update-shapes` above resizes the
|
||||
;; 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)]
|
||||
(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?
|
||||
(rx/concat
|
||||
|
||||
@@ -139,6 +139,12 @@
|
||||
:level :error
|
||||
: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
|
||||
[error]
|
||||
(if (and (string? (:hint error))
|
||||
@@ -240,8 +246,7 @@
|
||||
(st/async-emit! (rt/assign-exception error))))
|
||||
|
||||
;; This is a pure frontend error that can be caused by an active
|
||||
;; assertion (assertion that is preserved on production builds). From
|
||||
;; the user perspective this should be treated as internal error.
|
||||
;; assertion (assertion that is preserved on production builds).
|
||||
(defmethod ptk/handle-error :assertion
|
||||
[error]
|
||||
(when-let [cause (::instance error)]
|
||||
|
||||
@@ -57,13 +57,13 @@
|
||||
|
||||
:else
|
||||
(rx/throw
|
||||
(ex-info "repository request error"
|
||||
{:type :internal
|
||||
:code :repository-access-error
|
||||
(ex/error :type :internal
|
||||
:code :unable-to-process-repository-response
|
||||
:hint "unable to process repository response"
|
||||
:uri uri
|
||||
:status status
|
||||
:headers headers
|
||||
:data body}))))
|
||||
:data body))))
|
||||
|
||||
(def default-options
|
||||
{:update-file {:query-params [:id]}
|
||||
@@ -156,11 +156,11 @@
|
||||
tpoint (ct/tpoint-ms)]
|
||||
|
||||
(when (and response-stream? (not stream?))
|
||||
(ex/raise :type :internal
|
||||
:code :invalid-response-processing
|
||||
(ex/raise :type :assertion
|
||||
:code :unexpected-response
|
||||
:hint "expected normal response, received sse stream"
|
||||
:response-uri (:uri response)
|
||||
:response-status (:status response)))
|
||||
:uri (:uri response)
|
||||
:status (:status response)))
|
||||
|
||||
(if response-stream?
|
||||
(-> (sse/create-stream body)
|
||||
|
||||
@@ -44,8 +44,8 @@
|
||||
(rx/end! subs)))
|
||||
(obj/set! image "crossOrigin" "anonymous")
|
||||
(obj/set! image "onerror" #(rx/error! subs %))
|
||||
(obj/set! image "onabort" #(rx/error! subs (ex/error :type :internal
|
||||
:code :abort
|
||||
(obj/set! image "onabort" #(rx/error! subs (ex/error :type :abort
|
||||
:code :operation-aborted
|
||||
:hint "operation aborted")))
|
||||
(obj/set! image "src" uri)
|
||||
(fn []
|
||||
|
||||
@@ -106,10 +106,10 @@
|
||||
(p/catch
|
||||
(fn [cause]
|
||||
(vreset! abortable? false)
|
||||
(when-not @unsubscribed?
|
||||
(when-not (or @unsubscribed? (= (.-name ^js cause) "AbortError"))
|
||||
(let [error (ex-info (ex-message cause)
|
||||
{:type :internal
|
||||
:code :unable-to-fetch
|
||||
:code :fetch-error
|
||||
:hint "unable to perform fetch operation"
|
||||
:uri uri
|
||||
:headers headers}
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
(obj/set! reader "onerror"
|
||||
#(rx/error! subs %))
|
||||
(obj/set! reader "onabort"
|
||||
#(rx/error! subs (ex/error :type :internal
|
||||
:code :abort
|
||||
#(rx/error! subs (ex/error :type :abort
|
||||
:code :operation-aborted
|
||||
:hint "operation aborted")))
|
||||
(f reader)
|
||||
(fn []
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"Helpers for make zip file."
|
||||
(:require
|
||||
["@zip.js/zip.js" :as zip]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.util.array :as array]
|
||||
[promesa.core :as p]))
|
||||
|
||||
@@ -27,9 +28,9 @@
|
||||
(reader (js/Uint8Array. blob))
|
||||
|
||||
:else
|
||||
(throw (ex-info "invalid arguments"
|
||||
{:type :internal
|
||||
:code :invalid-type}))))
|
||||
(ex/raise :type :assertion
|
||||
:coce :invalid-type
|
||||
:hint "invalid data received for zip/reader")))
|
||||
|
||||
(defn blob-writer
|
||||
[& {:keys [mtype]}]
|
||||
@@ -62,10 +63,9 @@
|
||||
(.add writer path (new zip/TextReader content))
|
||||
|
||||
:else
|
||||
(throw (ex-info "invalid arguments"
|
||||
{:type :internal
|
||||
:code :invalid-type}))))
|
||||
|
||||
(ex/raise :type :assertion
|
||||
:code :invalid-type
|
||||
:hint "invalid data received for zip/add fn")))
|
||||
|
||||
(defn get-entry
|
||||
[reader path]
|
||||
|
||||
Reference in New Issue
Block a user