diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 187778913b..159eff63df 100644 --- a/CONTRIBUTING.md +++ b/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: ``` - +:emoji: [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 +Signed-off-by: Your Real Name ``` -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. diff --git a/common/src/app/common/exceptions.cljc b/common/src/app/common/exceptions.cljc index e104be775b..a07eda8c0e 100644 --- a/common/src/app/common/exceptions.cljc +++ b/common/src/app/common/exceptions.cljc @@ -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)))))) diff --git a/frontend/playwright/data/workspace/get-file-13755-fragment.json b/frontend/playwright/data/workspace/get-file-13755-fragment.json new file mode 100644 index 0000000000..ccfb4561a0 --- /dev/null +++ b/frontend/playwright/data/workspace/get-file-13755-fragment.json @@ -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" + } +} diff --git a/frontend/playwright/data/workspace/get-file-13755.json b/frontend/playwright/data/workspace/get-file-13755.json new file mode 100644 index 0000000000..9234febfe2 --- /dev/null +++ b/frontend/playwright/data/workspace/get-file-13755.json @@ -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" + } + } +} diff --git a/frontend/playwright/ui/specs/workspace-modifers.spec.js b/frontend/playwright/ui/specs/workspace-modifers.spec.js index 4b0fbb6f87..8e5f871fd8 100644 --- a/frontend/playwright/ui/specs/workspace-modifers.spec.js +++ b/frontend/playwright/ui/specs/workspace-modifers.spec.js @@ -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"); +}); diff --git a/frontend/src/app/main/data/auth.cljs b/frontend/src/app/main/data/auth.cljs index a933b83590..e3aa763ad6 100644 --- a/frontend/src/app/main/data/auth.cljs +++ b/frontend/src/app/main/data/auth.cljs @@ -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))))) diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index af03561722..1690398f8a 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -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 diff --git a/frontend/src/app/main/errors.cljs b/frontend/src/app/main/errors.cljs index 2327911e6f..63402ae3f2 100644 --- a/frontend/src/app/main/errors.cljs +++ b/frontend/src/app/main/errors.cljs @@ -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)] diff --git a/frontend/src/app/main/repo.cljs b/frontend/src/app/main/repo.cljs index 0ad10286aa..ad252e2a04 100644 --- a/frontend/src/app/main/repo.cljs +++ b/frontend/src/app/main/repo.cljs @@ -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) diff --git a/frontend/src/app/rasterizer.cljs b/frontend/src/app/rasterizer.cljs index e2bf9ede6e..fa1e233df4 100644 --- a/frontend/src/app/rasterizer.cljs +++ b/frontend/src/app/rasterizer.cljs @@ -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 [] diff --git a/frontend/src/app/util/http.cljs b/frontend/src/app/util/http.cljs index 7ed68dbc7d..8b9c590895 100644 --- a/frontend/src/app/util/http.cljs +++ b/frontend/src/app/util/http.cljs @@ -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} diff --git a/frontend/src/app/util/webapi.cljs b/frontend/src/app/util/webapi.cljs index 0833ac70d0..1b3a63b97a 100644 --- a/frontend/src/app/util/webapi.cljs +++ b/frontend/src/app/util/webapi.cljs @@ -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 [] diff --git a/frontend/src/app/util/zip.cljs b/frontend/src/app/util/zip.cljs index ae0d484133..2b8e67ce8a 100644 --- a/frontend/src/app/util/zip.cljs +++ b/frontend/src/app/util/zip.cljs @@ -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]